第十天
权限修饰符
public protected default private
同一类 true true true true
同一包 true true true false
子父类(不同包) true true flase false
不同包 true false false false
1.修饰类中的成员(方法和变量)
2.修饰类:public:任何地方都可以访问,在其他剧中使用需要导包!
default:只有在同一个包中访问!
权限修饰符:设置成员类的访问范围!
创建 Object的对象:
Object是所有类的父类
1.toString():返回对象的地址,如果想换,就重写 父类的方法
2.equals():比较两个对象是否是同一个对象!,比较对象的地址!
第十一天
==:比较的是地址(引用数据类型)
equals:比较的是 字符串的值
没有new对象
字符串保存在字符串常量池(字符串常量区),当你需要给一个字符串变量赋值时候,会先到字符串常量区中查找,如果没有就创建一个,在将所存的地址赋值给字符串变量!如果有就不在创建,直接将地址给变量。
new对象
字符串也是保存在字符串常量池(常量区),当你需要给一个字符串变量赋值的时候,先到字符串常量池中查找,如果没有就在常量池中创建一个 ,将值赋值到堆中的对象 ,如果有就不再创建,将值赋值到堆中的对象 。
截取字符串:String str = new String("字符数组char",截取的
开始的值,截取到哪里)
int length() 获取字符串的长度
char charAt(int index) 获取特定位置的字符 (注意角标越界)
int indexOf(String str) 获取特定字符的位置(overload)
int lastIndexOf(int ch) 获取最后一个字符的位置
类方法运行 String.valueof(转化)将其他的类型转换为字符串
char[] s= 字符串.toCharArray()将字符串转化为字符数组
s.replace("需要替换的字符","替换的字符"); 替换字符串,返回的是一个新的字符串
s.split(切割的符号) 切割字符串,返回是String数组
s.substring(截取开始的位置,截取结束的位置)截取字符串
s.toUpperCase()字符转大写
s.toLowerCase()字符转小写
s.trim()去除空格!
第十二天
StringBuffer :字符串缓存区:存在一个字符数组,默认会有一个大小:16字符,如果超了就会翻一倍,类似于oc中的 可变数组
StringBuffer:具有容器的特性:
1.增加!
StringBuffer str = new StringBuffer();
str.append("字符串");
2.插入!
str.insert(某一位置插入(后一位),插入的字符串);
3.删除 !
str.delete(开始删除的位置(包含起始),结束删除的位置(不包含结束));
deleteCharAt(删除指定位置的字符串)
4.修改 !
str.replace(开始的位置,结束的位置,替换的字符串);
5.翻转字符串!
str.reverse();
6.替换指定位置的字符!
str.setCharAt(指定的位置,替换的字符);
7.截取字符串!
str.substring(开始的位置);
str.substring(开始的位置,结束的位置);
8.查找!
str.indexOf(需查找的字符串);查找小的字符串在大的字符串的位置
str.lastIndexOf(字符串);查找最后一个
str.lastIndexOf(字符串,指定位置);从指定位置开始查找
str.toString()将String变为String
str.charAt(位置)查找指定的字符
StringBuffer 和 StringBulider 的使用方式是一样的!
相同点:1.两个类都是字符缓冲类!
2.两个类的方法一样
不同点:1.StringBuffer线程使用更安全,效率低;StringBulider线程不安全,效率高
2.StringBuffer是jdk1.0就有了,StringBulider1.5才有,推荐StringBulider
System的使用:
System是系统类,主要用途是用来获取系统的数据 ,无参且不能被实例化,里面的方法都是静态方法!
1.复制数组:arraycopy(需要复制的数组对象,复制的起始位置,复制到的数组,复制到数组的起始位置(后一位!),指定数据的长度);
例:
int[] arr = {1,3,4,7,8,9};
int[] arr1 = new int[20];
arr1[0] =4;
arr1[1] = 5;
arr1[2] = 6;
System.arraycopy(arr,3,arr1,3,3);
2.返回以毫秒为单位的当前时间:currentTimeMillis();
3.终止jvm:System.exit(0);其实调用的是Runtime.exit();方法!!
4.运行垃圾回收器:System.gc();
5.确定当前系统的属性:getProperties();
例:
Properties priperties = System.getProperties();
priperties.list(System.out);
获取操作系统:以键:值 存在
System.out.println(priperties.getProperty("os.name"));
在Java中最少有两个线程:1.主线程 2.垃圾回收线程
Runtime:该程序代表了运行的环境,在程序中是单例
1.getRuntime():返回当前程序运行的环境变量对象。
例:Runtime run = Runtime.getRuntime();
2.exec():根据指定的路径来执行对应的可执行文件!
例:Process process = run.exec(路径);
3.destroy():杀掉子进程的可执行的文件!
例:process.destroy();
4.exit():退出jvm虚拟机!
例:run.exit();
5.freeMemory():返回虚拟机的内存
例:System.out.println(“虚拟机内存还剩:”+run.freeMemory());
maxMemory();试图使用最大的内存
totalMemory();获取总内存!
日期类的学习:jdk1.1之前是Date,jdk1.1之后使用的是Calendar类来代替Date
date:获取系统当前的时间,精确到毫秒
获取当前时间:Date date = new Date();
1.getDate()返回多少号,弃用
Calendar:是一个抽象的类不能直接创建对象的!
例:Calendar cl = Calendar.getInstance();创建一个日历单例
System.out.println("年"+cl.get(Calender.YEAR));获取年份
System.out.println("月"+(cl.get(Calender.MONTH)+1));获取月份,十二进制 0-11
日+ Calender.DATE 获取日
星期+ ((Calender.DAY_OF_WEEK)-1);获取星期,七进制 0-6
时+ Calender.HOUR_OF_DAY;获取小时
分+ Calender.MINUTE;获取分钟
秒+ Calendar.SECOND;获取秒
System.out.println(cl.getTimeInMillis());返回1970到现在的毫秒数
SimpleDateFormat:时间的打印格式
1.将一个时间通过制定的格式以字符串形式输出
2.将一个字符串也是以指定的格式转为时间类型
例:
Date date = new Date();
指定一格式
String formate = "yyyy年MM月dd日 hh:mm:ss";
创建格式化对象:
SimpleDateFormate dateF = new SimpleDateFormate(formate);
String time = dateF.format(date);
打印时间
System.out.println(time);
定义字符串时间要与格式化格式一样
String dateStr = “时间”;
Date newDate = dateF.parse(dateStr);
System.out.println(newDate);
finalize 是程序或对象在被gc回收时调用此方法
第十三天
Math:数学类
Math.abs(数字):返回绝对值
Math.ceil(数字(duoble))向上取整!
Math.floor(数字(double)):向下取整
Math.round(数字):四舍五入
(int)(Math.random()*范围):产生随机数
Random random = new Random(10);产生0-9的随机数
random.nextInt(10);同上!
Array:用来存同一种数据类型的容器;
Object【】 arr:可以存放任意类型的对象!
1.但比较浪费内存,而且容量是固定的,无法随元素的变化而变化!
2.存放的数据类型不统一
集合:用来存放对象的容器,对象类型是任意的,长度可变!collection!
集合与数组的区别:
数组和集合类都是容器
数组长度是固定的,集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象数组中存储数据类型是单一的,集合中可以存储任意类型的对象。
集合类的特点
用于存储对象,长度是可变的,可以存储不同类型的对象。集合中只能存对象!
集合的结构:
collection 接口 根接口 集合的公共都在接口中! 单列集合
----》List 接口 可以有重复元素的集合,有序
List特有的方法: 接口
---->ArrayList:存在一个数组(Object[]),添加、删除元素慢,查找快
元素在内存中是有序的!默认的数组长度是Object[10];当不够时会增加为原来的1.5倍,涉及到了增容和数组的拷贝所以较慢
ArrayList的特有方法:
ArrayList list = new ArrayList();
ensureCapacity(容量);手动增加容量
trimToSize();调整容量刚好符合元素的个数
---->LinkedList:添加元素快,删除、查找慢:链式的结构!!
元素在内存中是无序的,但打印是有序的
LinkedList的特有方法:
LinkedList list = new LinkedList();
list.add(对象);
list.addFlist(对象);
list.addLast(对象);
addFlist():添加位于第一个的元素
addLast();添加位于最后一个的元素
----》Set 接口 不可以有重复元素的集合,无序
集合的目的:增删改查更方便!
例:Collection是接口 需要通过实现类来创建对象,集合只能存对象
Collection coll = new ArrayList();
增加:
1:add()将指定对象存储到容器中,add 方法的参数类型是Object便于接收任意对象
coll.add(10(对象));添加指定的对象
2:addAll()将指定集合中的元素添加到调用该方法和集合中
Collection coll1 = new ArrayList<>();
coll1.add("李四");
coll.addAll(coll1);将一个数组中的元素添加到另一个数组中
删除:
3:remove()将指定的对象从集合中删除
coll.remove("李四");从集合中删除指定的对象
4:removeAll()将指定集合中的元素删除
coll.removeAll(coll1);删除指定集合中的元素
修改
5:clear()清空集合中的所有元素
coll.clear();清空集合中的所有元素!
判断
6:isEmpty()判断集合是否为空
coll.isEmpty();返回boolean值,
7:contains()判断集合何中是否包含指定对象
coll.contains("元素");查找指定元素返回boolean
如果比较的是自定义对象 这时候需要重写equals方法和hashCode方法
其实也是调用可equalse();方法
8:containsAll()判断集合中是否包含指定集合
其实也是调用可equalse();方法
c.contatinsAll(conll);
获取: 9:int size()返回集合容器的大小
coll.size();返回有多少个!
转成数组10:toArray()集合转换数组
Object[] o = c.toArray();返回的是数组!
11.保留集合中相同的对象 retainAll()
c.retainAll(c1);保留两个集合中共有的对象,求交集
遍历集合元素!
1.将集合变为数组
Object[] o = c.toArray(可以指定对象开始变数组);
for(int i=0;i
System.out.println(o[i]);
}
List特有的方法:List中特有的方法都是通过下标来操作的!
---->ArrayList
---->LinkList
List list = new List<>();
插入
1.add(对象,指定位置);
addAll(指定位置,集合); 插入集合
list.add("张三");
list.add(0,"李四");
集合1.addAll(指定位置,集合2);
返回、获取
2.get(指定位置)
System.out.println(list.get(位置));
打印集合中的对象
查找
3.IndexOf();查找指定的对象在集合中第一个出现的位置:返回位置
System.out.println("是否存在对象:在什么位置:"+list.indexOf("对象"));
4.lastIndexOf();查找指定的对象在集合中最后一次出现的位置:返回位置
System.out.println("是否存在对象:在什么位置:"+list.lastIndexOf("对象"));
5.listIterator();迭代器:用于操作集合中的元素,增删,获取集合中的对象!
使用的注意事项:
1.当用迭代器来操作集合元素时,不要集合来操作元素!java.util.ConcurrentModificationException:不允许在用迭代器时用集合
2.
Iterator:迭代器的超级接口:所有的迭代器都继承于Iterator
迭代器默认指向第一个元素(指针);当调用next()方法后指针就会下移一位,不再指向第一个!
迭代器中常用的方法:
例:Collection c = new ArrayList<>();
c.add("狗娃");..... 添加元素
Iterator it = c.iterator();
1.hasNext();判断当前指针是否存在元素,返回boolean
System.out.println("当前指针是否存在元素?"+it.hasNext());
2.next();获取当前指针指向的元素,当被调用是就会下移
while(it.hasNext()){
System.out.println(it.next());
}
3.remove();移除最后一次操作的元素
it.remove()
ListItreaor:特有的方法
例:List list = new List();
list.add(“”);.....
1.获取迭代器对象
ListIterator it = list.listIterator();
2.hasPrevious();判断当前指针上一个是否存在元素
it.hasPrevious();获取当前指针指向的上一个元素,返回boolean
3.previous();获取上一个值,指针有上移了一位
it.previous();
倒序遍历集合中的元素
1.首先需要指针移动到最后一个元素的下方
while(it.hasNext()){
it.next();
}
while(it.hasPrevious()){
System.out.println(it.prevopis());
}
4.previousIndex();返回上一个指针的索引值
System.out.println(it.previousIndex());
5.add();通过迭代器的指针的位置来添加元素,添加到当前的指针
it.add(“对象”);
6.set();替换元素
it.set(对象);,替换当前指针指向的元素,最好不要与add()一起用,当用时需要it.next(),否则指针会不知道指哪里
更新
6.set(指定位置,替换的对象);更换指定位置的对象
list.set(位置,对象);
删除
7.remove(指定位置);删除指定索引位置!
list.remove(list.size()-1);
8.List.subList(开始,结束);截取集合的某一段,生成一个新的集合
List.subList(开始,结束)将指定的位置保留为另外一个集合。
List newList = list.subList(开始的位置,结束的位置);
遍历集合
for(int i = 0;i
System.out.println(list.get(i));
}
第十四天
当想让对象打印成员变量时可复写toString()方法!
1.LinkedList:
LinkedList list = new LinkedList();
list.add();.....
list.addFirst(“对象”);添加第一个
list.addLast();添加最后一个
list.getFirst();输入第一个;
list.getLast();输出最后一个
list.removeLast();删除最后一个,返回 最后一个元素
list.removerFirst();删除第一个,返回 最后一个元素
当集合中没有元素时会抛出java.util.NosuchElementException这个异常
2.数据节构
1.栈:先进后出
push();当被推入时会被推入到第一的位置!在栈中
list.push(对象);往集合中的堆栈中推入一个对象
pop();当推出时会移除最上一位的对象!在栈中
当push()的被去除完后就会变成ArrayList的顺序来----》小的先走,大的后走
list.pop();在集合中的堆栈中推出一个对象
2.队列:先进先出
offer();按顺序推入到集合中
list.offer();添加到最后一个!
poll();推出ArrayList的第一个
list.poll();推出第一个
3.获取逆序的迭代器:descendingIterator
Iterator it = list.descendingIerator();返回的迭代器时逆序的状态!
while(it.hasNext()){
System.out.println(it.next());
}
Vector: 描述的是一个线程安全的ArrayList 使用跟ArrayList一样
Vector与ArrayList的区别:
相同点:底层都是使用Object[]数组来实现
不同点:1.ArrayList是线程不同步,操作效率高!
Vector是线程同步的,操作效率低
2.ArrayList是在jdk1.2出现的,Vector是在jdk1.0出现的!
Set集合:也是接口,继承与Collection接口
特点:无序的元素不能重复
---->HashSet():
存值原理:存在于哈希表中,会通过调用对象的hashCode(先)和equals(后)方法获取哈希值,然后用移位的运算,获取哈希表的位置值!
情况一:如果我计算的位置上没有任何元素,将对象放在位置上
情况二:如果计算的位置上已经存在对象,这时候就会将要存的对象与已存在的对象作比较,如果equals返回的是true :对象就是重复元素,如果是false就在原来的位置上添加!
哈希表中是以桶式的结构来存放数据,一格可存放多个对象!
例:复写HashCode(){
如果是string就返回this.成员变量.hashCode()值(成员变量!)
int就返回this.成员变量
return 自定义的哈希;
}
复写equals(){
如果是string就返回(this.对比值).equals(obj.成员变量)
int就返回this.成员变量==obj.成员变量
return 自定义的值是否相等
}
例:
Set set = new HashSet();
set.add(对象);.....当添加的元素重复时无法进行添加!返回Boolean值
打印的是无序的!!
遍历Hashset()集合
1.集合变为数组遍历
2.迭代器遍历(无序:添加的顺序和打印出来的顺序是不一样的)
---->TreeSet();使用元素的自然顺序对元素进行排序.底层使用二叉树实现
注意点:存进去的对象需要具备自然排序的特性,取第一个字符根据阿斯特码(ASCII)来排!
1.往TreeSet添加对象的时候,如果对象有自然排序的特性,就按照这个自然排序进行排序!
2.往TreeSet添加对象时,如果对象本身不具备自然排序的特性,运行直接报错,如果要储存对象,那么对象的类必须要实现一个Comparable 接口的compareTo方法!
public int compareTo(Object o){
Book b =(Book)o;
return this.对比成员-b.对比的成员;
}
3.往TreeSet添加对象时,如果对象本身不具备自然排序的特性,并且没有实现Comparable 接口,那么这个时候就需要创建一个TreeSet类的时候传入一个比较器。
比较器的定义方式
TreeSet set = new TreeSet(new sunComp(创建比较器));在main或别的类的时候
比较的类(比较器也是一个类)必须与Compartor相接
class sunComp(类名)implements Compartor{
复写此方法!
public int compare (Object o1,Object o2){
o1,o2集合中的对象:book
Book b1= (Book)o1;
Book b2 = (Book)o2;
return b1.对比的值-b2.对比的值;
}
}
4.如果类中实现了Comparable的接口,又创建TreeSet时传入了一个比较器,这时候以比较器为标准
例:
TreeSet set = new TreeSet();
set.add("对象");
会自动的将对象排序!
字符串的比较规则:
1.获取字符串的第一个值进行比较,通过自然排序比较,如果都是一样的就比较下一个值还是一样的就再比较下一个,直到无法再比较
2.这个时候就会比较字符串的长度!
第十五天
二叉树:红黑树规则:左小右大!
泛型:确定集合中只能存放某一种数据类型的对象,jdk1.5之后推出的
优点:1.将运行时的错误提前到编译时。2.避免了无谓的强制转换!
如果传进去的是基本数据类型,接收时应用改用它的包装类来接收!
int--->Integer;
short--->Short;
double--->Double;
float--->Float;
byte--->Btey;
boolean--->Boolean;
long--->Long;
char--->Charactor
ArrayList<这个就是泛型> = new ArrayList<这个也是>();
自定义泛型方法:就是一个数据类型的 占位或一个数据类型变量,一般用T(Typt)或E(Element)来做占位符,占位符可以随意写,必须要满足遵守标识符的命名规范!
注意:1.泛型方法中自定义一个泛型数据类型实在实际参数传递时被确定的
2.泛型所用标识符需要符合标识符的命名规范!一般用大写!
格式:public static T 方法名(T s){
return s;
}
接收:String s = 方法名("传进去的对象");
定义一个泛型类:
定义格式:
class 类名<声明自定义的泛型>{}
泛型类的使用注意点:
1.泛型类定义的自定义泛型的类型是在创建泛型类对象时确定的!
2.如果一个自定义泛型类在创建时候没有指定类型,默认为Object类型!
3.静态方法是不能够使用自定义类的泛型,只能在静态方法中再次写一个泛型!
泛型接口的定义方式
interface 接口名<声明自定义的泛型>{}
class 类名 implements 接口名<指定类型>{}
泛型接口注意点:
1.接口上自定义的泛型是在实现接口时被指定的!
2.如果接口上没有指定接口,默认为Object类型!
3.如果需要创建接口实现类对象是指定数据类型,
那么需要格式:class 类名 <声明自定义泛型> implements 接口<声明自定义泛型>{}
Java可以后台数据管理也可以前端!!
Map:集合 接口 双列集合 K:V 键值对 OC中的字典很象!
特点:存储数据是以键和值的方式,键不允许重复,值是允许重复的!
----->HashMap:基于哈希表,而且是无序的!
使用注意点:
1.键可以是任意的对象!,值也可以是任意的对象!
2.Map集合中也可以存集合(嵌套集合)(List、Map);
3.当键相同时后面的Value会覆盖前面的Value
储存原理:也是使用哈希表来存放!
往HashMap添加元素,首先会调用键的HashCode()方法 获得一个哈希值,然后经过运算获取一个位置:情况有:
1.如果位置没有元素,就直接将元素存放在该位置
2.如果位置上有元素,就会调用元素的equals()方法与这个位置上的元素做比较,如果返回是true那么就被视为相同的键 就不存了;false就是不同的键 就存该元素
----->TreeMap:二叉树的结构储存。特点:以键来做自然排序的特性!
使用注意点:
1.往TreeMap添加的元素时,如果元素的键具备自然排序,那么就会通过自然排序对元素进行排序!
2.如果不具备自然排序特性,键所属的类必须实现Comparable 接口,把键的比较规则定义在compareTo()方法中!
3.如果不具备自然排序特性,也没有实现Comparable接口,创建TreeMap的时候给其一个比较器
结构:
class 类名 implements Compartor 接口{
如果返回的是负数就是在后面
是1就是在前面
0就是不添加!
}
键的比较规则定义在compare方法!
----->HashTable:HashMap一样的使用,线程安全,访问比较慢(了解!)
Map常用方法:
Map map = new HashMap();
添加:
put(K,V);
map.put(键(可存对象),值(可存对象));
putAll(K,V(集合));A.putAll(B);将B中的元素添加到A中
Map map2 = new HashMap();
map.putAll(map2);
删除:
clear();
map.clear();清空Map集合中所以的元素!
remove();
map.remove(键);通过键删除指定的元素!
获取:
get(键);
map.get(键);通过键来获取值!
size();
map.size();返回map集合里面的元素个数!
获取键的方法
判断:
isEmpty();判断集合是否为空!
map.isEmpty();判断集合中是否为空!false是不为空!
containsKey(K);
map.containsKey(键);判断是否存在某一个键!有返回true
containsValue(V);
map.containsValue(值);判断是否含有某一个值!需要复写equals()和HashCode()方法!
Map的遍历方式:
迭代器遍历:
entrySet();
KeySet();
values();
例:
1.定义Map
Map map = new HashMap();
2.添加元素
map.put("范冰冰","李晨");
3.遍历
map遍历元素方式一:keySet();获取所有的键用Set集合来保存!
特点:通过遍历键,通过键来取值
Set set = map.keySet();
Iterator it = set.iterator();
while(it.hashNext()){
String key = it.next();
System.out.println("键"+key+“:值”+map.get(key));
}
map遍历元素方式二:values():获取map集合中所有的值,用Collection集合保存
Collection coll = map.values();
Iterator it = coll.iterator();
while(it.hashNext()){
String value = it.next();
System.out.println(“值:”+value);
}
map遍历元素方式三:entrySet()
Set> entry = map.entrySet();
Iterator> it = entry.iterator();
while(it.hashNext()){
Map.Entry entry = it.next();
Map.Entry:接口:提供给用户操作map集合
getKey();获取键
getValue();获取值
setValue();更改值
System.out.println("键:"+entry.getKey()+"值:"+entry.getValue());
}
Collection:接口
Collections:集合的工具类
Collection与Collections的区别?
Collection 是一个单列集合的根接口,Collections是操作集合的工具类!只能操作list集合 !
Collections中常有的方法
Collections:常见方法:
Collections.sort();排序
1, 对list进行二分查找:
前提该集合一定要有序。
int binarySearch(list,key);
Collections.binarySearch(集合,"对象");返回索引的位置 没找到返回是一个负数,索引
//必须根据元素自然顺序对列表进行升级排序
//要求list 集合中的元素都是Comparable 的子类。
int binarySearch(list,key,Comparator);
2,对list集合进行排序。
sort(list);
//对list进行排序,其实使用的事list容器中的对象的compareTo方法
sort(list,comaprator);
//按照指定比较器进行排序
3,对集合取最大值或者最小值。
max(Collection)
max(Collection,comparator)
min(Collection)
min(Collection,comparator)
4,对list集合进行反转。
reverse(list);
5,对比较方式进行强行逆转。
Comparator reverseOrder();
Comparator reverseOrder(Comparator);
6,对list集合中的元素进行位置的置换。
swap(list,x,y);
7,对list集合进行元素的替换。如果被替换的元素不存在,那么原集合不变。
replaceAll(list,old,new);
8,可以将不同步的集合变成同步的集合。
Set synchronizedSet(Set s)
Map synchronizedMap(Map m)
List synchronizedList(List list)
9.如果想要将集合变数组:
可以使用Collection 中的toArray 方法。注意:是Collection不是Collections工具类
传入指定的类型数组即可,该数组的长度最好为集合的size。
第十六天
进程:就是正在运行的程序,作用是分配内存让应用程序能够运行!
windows系统号称多任务(可以同时运行多个应用程序)。
宏观上:windows确实是运行的多个程序
微观上:CPU快速作了切换操作,肉眼不可察
线程:线程在一个进程中负责代码的执行!就是一个进程的执行路径!
Java程序在运行的时候,jvm会帮我们创建一个主线程和一个垃圾回收器(也是一个线程)。主线程主要负责main方法中的代码执行。垃圾回收器负责垃圾回收!main是主线程!
一个Java程序中至少有两个线程。
多线程:在一个进程中有多个线程存在并且线程“同时”执行不同的任务。“同时”:单核CPU快速切换多个线程执行任务,速度很快,肉眼不可察
好处:1.可以解决一个进程中同时执行多个任务的问题
2.可以提高资源的利用率
坏处:1.增加CPU的负担!不是线程越多越好
2.降低进程中线程的执行效率。
3.会引发线程安全问题
4.容易出现死锁
Java创建线程:
方式一:Thread(线程类)
1.需要定义一个类来继承Thread类
2.重写Thread类中的run方法!把自定义线程的任务代码写在run方法中。每一个线程都有自己的任务代码,jvm创建主线程的任务代码就是main,自定义想成的任务代码就写在run方法中!
class 类名 extends Thread{
public void run(){
//自定义线程的任务代码
for(int i=0;i<100;i++){
System.out.println(“多线程”+i);
}
}
}
3.创建Thread的子类,并且调用start开启线程!
在main中开启线程,
注:1.在main中代码的执行是从上到下,必须放在执行main的上面
2.一旦线程开启,就会默认执行线程对象的run方法,但是直接千万不要调用,如果直接调用run方法就跟普通方法没有区别!
类名 名字 = new 类名();
名字.start();
for(int i=0;i<100;i++){
System.out.println(“主线程”+i);
}
方式二:推荐!由于Java是单继承,所以就不能继承Thread和该继承的类,但接口可以有多个!
1.自定义一个类实现Runable接口,接口就会提供一个run方法!
2.实现Runable接口中的run方法。将线程中的任务写在run方法中
3.实现Runable接口的实现类对象!
4.创建一个Thread对象
5.调用Thread的对象的start方法!
class 类名 implements Runnable{
public void run(){
}
}
main函数:
类名 对象名 = new 类名();-----》不是线程对象!只有是Thread或者是Thread的子类才是线程对象!
Thread thread = new Thread(对象名);
thread.start();
为什么要将Runable接口实现类的对象作为参数传递?
让对象中的run方法能够在线程中的run方法中执行!
线程的生命周期:-
---->CPU的等待资格
---->CPU的执行权
1.创建一个线程:new 子线程类
2.等待运行状态,改状态只有CPU的等待资格:
3.运行状态,改状态具备了CPU的执行权与CPU的等待资格
4.线程结束,
线程的阻塞状态:可由运行状态转化,一个线程执行了sleep或者wait方法,线程会处于阻塞状态!如果是sleep方法,如果超过了睡眠时间线程会立马进入等待运行状态!如果是wait,就需要通过其他的线程来唤醒!
线程中常用的方法:
1.Thread(String name)初始化线程名字
main 函数中
类名 对象名 = new 类名(“线程名”);
对象名.setName(“更换线程名”);
Thread maint = Thread.currentThread();CPU当前执行的线程
System.out.println("获取线程的优先级"+maint.getPriority());
定义一个构造方法来调用父类的构造方法
public 类名(String name){
super(name);
}
类中重写run方法
public void run(){
执行的代码!睡眠后打印线程名!
try{异常只能处理:Thread父类的sleep方法没有抛出异常所以子类也不允许抛
this.sleep(毫秒);或Thread.sleep(“毫秒”);可以抛异常
}catch(异常){
。。。。
}
System.out.println(“线程名”+this.getName());
}
2.getName()返回线程的名字
3.setName(String name)设置线程对象名
4.sleep(毫秒)线程睡眠是静态的可以通过类方法调用!谁执行的Thread.sleep就睡眠谁
5.getPriority()返回线程对象的优先级默认是5,最高是10
6.Thread.currentThread();
CPU当前执行的线程,指的是正在执行的run方法的线程对象
this不是线程对象!
7.setPriority(10);给该对象设置线程的优先级
线程安全问题:
Java的线程锁:
方式一:同步代码块!!推荐
Java中的任意一个对象都会有一个对象的状态,就可以通过对象的状态作为锁的标识符
优点:1.同步代码块的锁对象可以由自己指定,同步函数是固定的的
2.同步代码块可以控制被同步的范围,同步函数必须是整个函数!
注意:1.任意对象都可以做锁对象
2.如果在同步代码块中调用sleep方法,锁对象不会被释放!
3.只有真正存在线程安全的时候才会需要使用同步代码块,否则会降低效率
4.多线程操作锁对象必须是唯一的,否则无效
static Object o = new Object(); 锁的对象应该是同一个对象static 或 单例
synchronized(锁对象 o){锁对象可以使任意Java中的对象,也可以是字符串
代码!!!
}
方式二:同步函数,用关键字sybnchronized修饰函数
同步函数是否有锁对象:有!
同步函数使用注意点:
1.如果是一个非静态的函数,同步函数的锁就是调用方法的对象(this对象),如果是一个静态函数同步函数的锁对象是当前函数所属类的字节码文件(Class对象)!
2.同步函数的锁对象固定,不能够自己来指定
3.同步函数是同步整个函数的代码块!
线程什么时候安全什么时候不安全?
出现线程安全的根本原因:
1.存在两个或两个以上的线程,并且线程之间共享着一个资源
2.多个语句同时操作的共享资源!
代码同步是可以解决线程安全问题!如果使用不当会导致线程的死锁!
A线程等B线程 ,B线程又在等A线程 结果两个人就一直等下去,这时候就造成了线程的死锁
线程死锁不一定会出现,有可能出现!
死锁的解决方法:尽量避免出现线程之间的等待问题!
public void run() {
if("张三".equals(Thread.currentThread().getName())){
synchronized ("遥控器") { //锁对象就锁住了
System.out.println("张三拿到了遥控器 ,准备去拿电池");
synchronized ("电池") {//已经被锁住了
System.out.println("张三拿到了遥控器和电池,开着空调,在也 不冷了");
}
}
}else if("老王".equals(Thread.currentThread().getName())){
synchronized ("电池") { //锁也被锁住了 电池对象的状态 变为锁住的状态
System.out.println("老王拿到电池,准备去拿遥控器");
synchronized ("遥控器") { //遥控器也被锁住了
System.out.println("老王拿到了遥控器和电池,开着空调,在也 不冷了");
}
}
}
}
线程的通讯:一个线程完成自己的任务,去通知另外一个线程去完成另外一个任务!
wait();等待如果线程执行了wait()方法,那么该线程就会处于等待状态,等待状态的线程必须通过其他线程来调用notify()方法来唤醒!通过锁对象来调用
try{
锁对象.wait();
}catch(){
}
notify();唤醒 随机唤醒一个
锁对象.notify();
notifyAll();唤醒全部线程
生产者和消费者
注意:1.wait和notify都是属于Object对象的
2.wait方法和notify方法必须在同步线程中使用
3.wait方法和notify方法必须由锁对象来调用!
线程的停止:
1.停止一个线程 一般通过一个变量来控制
2.如果需要停止一个处于等待状态的线程,应配合interrupt方法!
Thread.currentThread().stop();停止当前前程!已过时!此方法不安全
对象.interrupt();直接调用interrupt是没有用的
如何创建后台(守护)线程:在一个进程中只剩下后台(守护)线程,守护线程也会死掉!
线程默认的不是后台线程!将某个对象的线程标记为守护线程
对象.setDaemon(布尔值);true是守护 false一般线程
join方法 加入:可以在一个线程中,加入另外一个线程执行,前面的线程会等待加入的线程执行完毕后在执行!
run方法中加入另外一个线程
类名 对象名 = new 类名();
对象.start();
try{如果有一个线程执行join语句,有一个新的线程加入,在执行的线程需要让步新的线程执行完毕,然后才能执行
对象.joun();
}catch(){
}