包装类
包装类的分类
包装类和基本数据的转换
演示包装类和基本数据类型的相互转换,这里以int和Integer演示。
1)jdk5前的手动装箱和拆箱方式,装箱:基本类型->包装类型,反之,拆箱
2)jdk5以后(含jdk5)的自动装箱和拆箱方式
3)自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
4)其它包装类的用法类似,
//演示 int <--> Integer 的装箱和拆箱
//jdk5 前是手动装箱和拆箱
//手动装箱 int->Integer
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱 //Integer -> int
int i = integer.valueOf(n1);
//自动装箱 int->Integer
Integer integer2 = n2;
//底层使用的是
Integer.valueOf(n2)
//自动拆箱
Integer->int
int n3 = integer2;
//底层仍然使用的是valueOf
Integer主要是看范围 -128 ~ 127 就是直接返回 //,否则,就 new Integer(xx)
Integer 类和 Character
System.out.println(Integer.MIN_VALUE); // 返回最小值
System.out.println(Integer.MAX_VALUE);// 返回最大值
System.out.println(Character.isDigit('a'));// 判断是不是数字
System.out.println(Character.isLetter('a'));// 判断是不是字母
System.out.println(Character.isUpperCase('a'));// 判断是不是大写
System.out.println(Character.isLowerCase('a'));// 判断是不是小写
System.out.println(Character.isWhitespace('a'));// 判断是不是空格
System.out.println(Character.toUpperCase('a')); 转成大写
System.out.println(Character.toLowerCase('A'));// 转成小写
String 类
String 类的理解和创建对象
创建 String 对象的两种方式
1)方式一:直接赋值 String s= "wyx”
2)方式二:调用构造器 String s = new String("wyx");
区别
方式一:直接赋值 String s= "wyx";
方式二:调用构造器 String s2 = new String("wyx");
1.方式一:先从常量池查看是否有"wyx"数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址
2.方式二:先在堆中创建空间,里面维护了value属性,指向常量池的wyx空如果常量池没有"wyx",重新创建,如果有,直接通过value指向。最终指的是堆中的空间地址。
3.画出两种方式的内存分布图
字符串的特性
说明
1)String是一个final类,代表不可变的字符序列
2)字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的1.以下语句创建了几个对象?画出内存布局图。
String s1 = "hello";
s1="haha"; //1min
/创建了2个对象
小结:
底层是 StringBuilder sb = new StringBuilder();
sb.append(a); sb.append(b);sb是在堆中,
并且append是在原来字符串的基础上追加的
重要规则:
String c1 = "ab" + "cd";常量相加,看的是池。
String c1 = a+b;变量相加,是在堆中
String 类的常见方法
说明
String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低,因此jawa设计者还提供了StringBuilder 和 StringBuffer 来增强String的功能并提高效率。
String 类的常见方法一览
StringBuffer 类
基本介绍
- Java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
- 很多方法与String相同,但StringBuffer是可变长度的。
- StringBuffer是一个容器。
String VS StringBuffer
- String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低//private final char value[];
- StringBuffer保存的是字符串变量,里面的值可以更改,每次
- StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高/char[] value;//这个放在堆
String 和 StringBuffer 相互转换
//方式 1 使用构造器
//注意: 返回的才是 StringBuffer 对象,对 str 本身没有影响
StringBuffer stringBuffer = new StringBuffer(str);
//方式 2 使用的是 append 方法
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str)
StringBuffer ->String
//方式 1 使用 StringBuffer 提供的 toString 方法
String s = stringBuffer3.toString();
//方式 2: 使用构造器来搞定
String s1 = new String(stringBuffer3)
StringBuilder 类
1)一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder 不是线程安全)。该类被设计用作 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类因为在大多数实现中,它比StringBuffer要快[后面测]。
2)在 StringBuilder上的主要操作是 append 和 insert方法,可重载这些方法,以接受任意类型的数据。
3) StringBuilder 和 StringBuffer 均代表可变的字符序列,方法是一样的,所以使用和StringBuffer一样,
//1. StringBuilder 继承 AbstractStringBuilder 类
//2. 实现了 Serializable ,说明 StringBuilder 对象是可以串行化(对象可以网络传输,可以保存到文件)
//3. StringBuilder 是 final 类, 不能被继承
//4. StringBuilder 对象字符序列仍然是存放在其父类 AbstractStringBuilder 的 char[] value; // 因此,字符序列是堆中
//5. StringBuilder 的方法,没有做互斥的处理,即没有 synchronized 关键字,因此在单线程的情况下使用 // StringBuilder stringBuilder = new StringBuilder()
String、StringBuffer 和 StringBuilder 的比较
1)StringBuilder 和 StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
2)String:不可变字符序列,效率低,但是复用率高。
3)StringBuffer:可变字符序列、效率较高(增删)、线程安全,看源码
4)StringBuilder:可变字符序列、效率最高、线程不安全
5)String使用注意说明:
string s="a";//创建了一个字符串
s+="b";/实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能=>结论:如果我们对String做大量修改,不要使用String
String、StringBuffer 和 StringBuilder 的选择
Math 类
基本介绍
Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数
方法一览
Arrays 类
Arrays 类常见方法
System 类
System 类常见方法
1)exit退出当前程序
2)arraycopy:复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组
int[] src={1,2,3};
int[] dest = new int[3];
System.arraycopy(src, 0, dest, 0, 3);
3)currentTimeMillens:返回当前时间距离1970-1-1的毫秒数
4)gc:运行垃圾回收机制 System.gc();
BigInteger 和 BigDecimal 类
应用场景:
1)Biglnteger适合保存比较大的整型
2)BigDecimal适合保存精度更高的浮点型(小数)
BigInteger BigDecimal常见方法
1)add加 2)subtract减
3)multiply乘 4) divide除
日期类
第一代日期类
第二代日期类
- 第二代日期类,主要就是Calendar类(日历)。
- public abstract class Calendar extends Object implements Serializable,Cloneable Comparable Calendar
- Calendar类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY OF MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法
第三代日期类
DateTimeFormatter 格式日期
DateTimeFormat dtf = DateTimeFormatter.ofPattern(格式);
String str = dtf.format(日期对象);
类似于Date
提供了一系列和Date类转换的方式Instant->Date:
Date date = Date.from(instant);Date->Instant:
Instant instant = date.tolnstant):
第三代日期类更多方法
LocalDateTime类
MonthDay类:检查重复事件
是否是闰年
增加日期的某个部分
使用plus方法测试增加时间的某个部分
使用minus方法测试查看一年前和一年后的日期
集合
1)可以动态保存任意多个对象,使用比较方便!
2)提供了一系列方便的操作对象的方法:add、remove、set、get等3)使用集合添加,删除新元素的示意代码-简洁了
集合的框架体系
Collection 接口和常用方法
Collection 接口实现类的特点
1)collection实现子类可以存放多个元素,每个元素可以是Object
2)有些Collection的实现类,可以存放重复的元素,有些不可以
3)有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
4)Collection接口没有直接的实现子类,是通过它的子接口Set和 List来实现的
Collection 接口遍历元素方式 1-使用使用 Iterator(迭代器)
基本介绍
1)Iterator对象称为迭代器,主要用于遍历 Collection集合中的元素。2)所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
3)Iterator的结构.[看一张图]
4)Iterator 仅用于遍历集合,Iterator本身并不存放对象。
提示:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,
且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。
Collection 接口遍历对象方式 2-for
List 接口和常用方法
List 接口基本介绍
List接口是 Collection接口接口
1)List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
2)List集合中的每个元素都有其对应的顺序索引,即支持索引。
3)List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
JDKAPI中List接口的实现类:
List 接口和常用方法
常用的 ArrayList LinkedList和Vector
List 接口的常用方法
- boolean addAll(int index, Collection eles):从 index位置开始将 eles 中的所有元素添加
- Object get(int index):获取指定 index 位置的元素
- int indexOf(Object obj):返回 obj 在集合中首次出现的位置
- int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
- Object remove(int index):移除指定 index 位置的元素,并返回此元素
- Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换.
- List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
/ 注意返回的子集合 fromIndex <= subList < toIndex
List 的三种遍历方式 [ArrayList, LinkedList,Vector]
ArrayList 底层结构和源码分析
ArrayList 的注意事
1) permits all elements, including null , ArrayList 可以加入null,并且多个
2) ArrayList是由数组来实现数据存储的
3) ArrayList 基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码 在多线程情况下,不建议使用ArrayList
ArrayList 的底层操作机制源码分析(重点,难点.)
1)ArrayList中维护了一个Object类型的数组elementData. transient Object[] elementData; //transient表示瞬间,短暂的,表示该属性不会被序列号
2)当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
3)如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
示意图:
Vector 底层结构和源码剖析
Vector 的基本介绍
//如果 需要的数组大小 不够用,就扩容 , 扩容的算法
//newCapacity = oldCapacity + ((capacityIncrement > 0) ?
// capacityIncrement : oldCapacity);
//就是扩容两倍.
Vector 和 ArrayList的比较
LinkedList 底层结构
LinkedList 的全面说
1)LinkedList底层实现了双向链表和双端队列特点
2)可以添加任意元素(元素可以重复),包括null
3)线程不安全,没有实现同步
LinkedList底层操作机制
1)LinkedList底层维护了一个双向链表
2)LinkedList中维护了两个属性first和last分别指向首节点和尾节点
3)每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
4)所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
模拟一个简单的双向链表
LinkedList 的增删改查案例
- linkedList.remove(); // 这里默认删除的是第一个结点
- linkedList.set(1, 999); //修改某个结点对象
- /get(1) 是得到双向链表的第二个对象Object o = linkedList.get(1)
- //因为 LinkedList 是 实现了 List 接口, 遍历方式
System.out.println("===LinkeList 遍历迭代器====");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println("next=" + next);
}
ArrayList 和 LinkedList 比较
Set 接口和常用方法
Set 接口基本介绍
1)无序(添加和取出的顺序不一致),没有索引
2)不允许重复元素,所以最多包含一个null
3)JDK APl中Set接口的实现类有:
Set 接口的常用方法
和List 接口一样, Set 接口也是 Collection的子接口 因此 常用方法和 Collection 接口一样.
Set 接口的遍历方式
1.可以使用迭代器
2.增强for
3.不能使用索引的方式来获取
Set 接口实现类-HashSet
HashSet 的全面说明
1)HashSet实现了Set接口
2)HashSet实际上是HashMap,
3)可以存放null值,但是只能有一个null
4)HashSet不保证元素是有序的,取决于hash后,再确定索引的结果.(即,不保证存放元素的顺序和取出顺序一致)
5)不能有重复元素/对象.在前面Set接口使用已经讲过
HashSet 底层机制说明
分析HashSet底层是HashMap, HashMap底层是(数组+链表+红黑树)
>分析HashSet的添加元素底层是如何实现(hash()+equals())
>分析HashSet的扩容和转成红黑树机制
Set 接口实现类-LinkedHashSet
LinkedHashSet的全面说明
1)LinkedHashSet是 HashSet的子类
2)LinkedHashSet底层是一个 LinkedHashMap,底层维护了一个数组+双向链表
3)LinkedHashSet根据元素的 hashCode值来决定元素的存储位置,同时使用链表维护元素
的次序(图),这使得元素看起来是以插入顺序保存的。
4)LinkedHashSet不许添重复元素
Map 接口和常用方法
Map 接口和常用方法
注意:这里讲的是JDK8的Map接口特点
1)Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value2)Map中的key和 value可以是任何引用类型的数据,会封装到HashMap$Node对象中
3)Map中的 key不允许重复,原因和HashSet一样,前面分析过源码
4)Map中的value可以重复
5)Map的key可以为 null, value也可以为null,注意 key 为null只能有一个,,,value为null,可以多个.
6)常用String类作为Map的 key
7)key和 value之间存在单向一对一关系,即通过指定的key总能找到对应的 value
8)Map存放数据的key-value示意图,一对k-v是放在一个HashMap$Node中的,有因为Node实现了 Entry接口,有些书上也说一对k-v就是一个Entry(如图)
Map 接口常用方法
remove:根据键删除映射关系
get:根据键获取值
size:获取元素个数
isEmpty:判断个数是否为0
clear:清除 k-v
containsKey:查找键是否存在
Map 接口遍历方法
- containsKey:查找键是否存在
- keySet:获取所有的键
- entrySet:获取所有关系k-v
- values:获取所有的值
迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());//HashMap$Node实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue()
Map 接口实现类- HashMap
HashMap 小结
1)Map接口的常用实现类:HashMap、Hashtable和Properties。
2)HashMap是Map接口使用频率最高的实现类。
3) HashMap 是以 key-val 对的方式来存储数据(HashMap$Node类型)
4)key不能重复,但是值可以重复,允许使用null键和null值
5)如果添加相同的key,则会覆盖原来的key-val,等同于修改.(key不会替换,val会替换)
6)与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.(jdk8的hashMap底层数组+链表+红黑树)
7)HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
HashMap 底层机制及源码剖析
HashMap 底层机制及源码剖
>扩容机制[和HashSet相同]
1) HashMap底层维护了Node类型的数组table,默认为null
2) 当创建对象时,将加载因子(loadfactor)初始化为0.75.
3) 当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key相是否等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。
4) 第1次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)
5)以后再扩容,则需要扩容table容量为原来的2倍(32)临界值为原来的2倍,即24,依次类推
6) 在Java8中,如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8)并且table的大小 >= MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树)
Map 接口实现类-Hashtable
HashTable 的基本介绍
1)存放的元素是键值对:即K-V
2)hashtable的键和值都不能为null,否则会抛出NullPointerException3)hashTable使用方法基本上和HashMap一样
4) hashTable 是线程安全的(synchronized), hashMap 是线程不安全的
Hashtable 和HashMap对比
Map-Properties
基本介绍
1.Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
2.他的使用特点和Hashtable类似
3. Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象并进行读取和修改
总结-开发中如何选择集合实现类(记住)
Collections工具类
Collections工具类介绍
1)Collections是一个操作 Set、List和 Map等集合的工具类
2)Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作14.17.2排序操作:(均为static方法)
2.1)reverse(List):反转 List中元素的顺序
2.2)shuffle(List):对 List集合元素进行随机排序
2.3)sort(List):根据元素的自然顺序对指定 List集合元素按升序排序
2.4)sort(List,Comparator)根据指定的 Comparator产生的顺序对 List集合元素进行排序
2.5)swap(List, int, int):将指定 list集合中的i处元素和j处元素进行交换
排序操作:(均为 static 方法)
查找、替换
泛型
使用传统方法的问题分析
1)不能对加入到集合ArrayList中的数据类型进行约束(不安全)
2)遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
泛型的理解和好处
1)编译时,检查添加元素的类型,提高了安全性
2)减少了类型转换的次数,提高效率[说明]
3)不再提示编译警告
泛型介绍
理解:泛(广泛)型(类型) => Integer, String,Dog
1)泛型又称参数化类型,是Jdk5.0出现的新特性,解决数据类型的安全性问题
2)在类声明或实例化时只要指定好需要的具体的类型即可。
3)Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮
4)泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型
泛型的语法
泛型的声明
interface 接口<T>{}和 class类<K,V>{}
比如:List,ArrayList
说明:
1)其中,T,K,V不代表值,而是表示类型。
2)任意字母都可以。常用T表示,是Type的缩写
泛型的实例化
要在类名后面指定类型参数的值(类型)。
如:
- List<String> strList = new ArrayList<String>()
- List<String> strList = new ArrayList< >()
- Iterator<Customer> iterator = customers.iterator();
泛型使用的注意事项和细节
1. interface List<T>{}, public class HashSet<E>{).等等
说明:T,E只能是引用类型
看看下面语句是否正确?:
- List<Integer> list = new ArrayList<Integer>(); /OK
- List<int> list2 = new ArrayList<int>();/错误
2.在给泛型指定具体类型后,可以传入该类型或者其子类类型
3.泛型使用形式
- List<Integer> list1 = new ArrayList<Integer>();
- List<Integer> list2 = new ArrayList<>();
3.如果我们这样写 List list3 = new ArrayList();默认给它的泛型是[<E>E就是Object]
自定义泛型类
>基本语法
class类名<T,R.>{/.….表示可以有多个泛型
成员}
>注意细节
1)普通成员可以使用泛型(属性、方法)
2)使用泛型的数组 不能初始化 。
因为数组在 new 不能确定 T 的类型就无法在内存开空间
3)静态方法中不能使用类的泛型
因为静态是和类相关的,在类加载时,对象还没有创建所以,如果静态方法和静态属性使用了泛型,JVM 就无法完成初始化
4)泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
5)如果在创建对象时,没有指定类型,默认为Object
如:
1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
2, T, R, M 泛型的标识符, 一般是单个大写字母
3. 泛型标识符可以有多个
自定义泛型接口
>基本语法
interface接名<T, R..>{}
>注意细节
1)接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
2)泛型接口的类型,在继承接口或者实现接口时确定
3)没有指定类型,默认为Object
如:
自定义泛型方法
>基本语法
修饰符<T,R.>返回类型方法名(参数列表)
>注意细节
1.泛型方法,可以定义在普通类中,也可以定义在泛型类中
2.当泛型方法被调用时,类型会确定
3. public void eat(E e) {},修饰符后没有<T,R.> eat方法不是泛型方法,而是使用了泛型
泛型的继承和通配符
1)泛型不具备继承性
List<Object> list = new ArrayList<String>();错误示列
2)<?>:支持任意泛型类型
3)<?extends A>:支持A类以及A类的子类,规定了泛型的上限
4)<?super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
JUnit
为什么需要 JUnit
1.一个类有很多功能代码需要测试,为了测试,就需要写入到main方法中
2.如果有多个功能代码测试,就需要来回注销,切换很麻烦
3.如果可以直接运行一个方法,就方便很多,并且可以给出相关信息,就好了->Junit
基本介绍:
1.JUnit是一个Java语言的单元测试框架
2.多数Java的开发环境都已经集成了JUnit作为单元测试的工具
java 绘图坐标体系
下图说明了Java坐标系。坐标原点位于左上角,以像素为单位。
在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素
坐标体系 - 像素
1.绘图还必须要搞清一个非常重要的概念-像素一个像素等于多少厘米?
2.计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如,计算机显示器的分辨率是800×600,表示计算机屏幕上的每一行由800个点组成,共有600行,整个计算机屏幕共有480000个像素。像素是一个密度单位,而厘米是长度单位,两者无法比较
绘图原理
Component类提供了两个和绘图相关最重要的方法:
1.paint(Graphicsg)件外观
2.repaint()刷新件外。
当组件第一次在屏幕显示的时候,程序会自动的调用paint()方法来绘制组件
在以下情况paint()将会被调用:
1.窗口最小化,再最大化
2.窗口的大小发生变化
3.repaint方法被调用
Graphics 类
- 画直线 drawLine(int x1,int y1,int x2,int y2)
- 画矩形边框 drawRect(int x, int y, int width, int height)
- 画椭圆边框 drawOval(int x, int y, int width, int height)
- 填充矩形 fillRect(int x, int y, int width, int height)
- 填充椭圆 fillOval(int x, int y, int width, int height)
- 图片 drawlmage(Image imp, int x, int y, .)
- 画字符串 drawString(String str, int x, int y)
- 设置画笔的字体 setFont(Font font)
- 设置画笔的颜色 setColor(Color c)
java 事件处理机制
基本说明
java事件处理是采取“委派事件模型"。当事件发生时,产生事件的对象,会把此"信息”传递给"事件的监听者"处理,这里所说的"信息"实际上就是java.awt.event事件类库里某个类所创建的对象,把它称为“事件的对象"。
示意图
多线程基础
线程相关概念
自己理解:为了完成特定的任务的一组指令的集合
进程
1.是指运行中的程序
2.进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程
3.并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发。
4.并行:同一个时刻 多个任务同时执行。多核cpu可以实现并行。并发和并行可以切换
线程基本使用
创建线程的两种方式
- 继承Thread类,重写 run方法
start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现 //真正实现多线程的效果是 start0(), 而不是 run
2.实现Runnable接口,重写 run方法
1.java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了。
2.java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程’
Thread thread = new Thread(T) //因为接口不能直接调用得用类
thread.start();
线程如何理解
继承 Thread vs 实现 Runnable 的区别
1.从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
2.实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable
线程终止
基本说明
1.当线程完成任务后,会自动退出。
2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
常用方法第一组
- setName 设置线程名称,使之与参数 name相同
- getName 返回该线程的名称
- start 使该线程开始执行; Java虚拟机底层调用该线程的 start0方法
- run 调用程对象run方法;
- setPriority 更改线程的优先级
- getPriority 获取线程的优先级
- sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
- interrupt 中断线程
注意事项和细节
- start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
- 线程优先级的范围
- interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
- sleep:线程的静态方法,使当前线程休眠
常用方法第二组
- yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
- join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
线程的生命周期
JDK 中用 Thread.State 枚举表示了线程的几种状
线程状态转换图
线程的同步
Synchronized线程同步机制
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
- 也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
同步具体方法-Synchronized
分析同步原理
互斥锁
基本介绍
1.Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
3.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率要降低
5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
6.同步方法(静态的)的锁为当前类本身。
注意事项和细节
1.同步方法如果没有使用static修饰:默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
- 需要先分析上锁的代码
- 选择同步代码块或同步方法
- 要求多个线程的锁对象为同一个即可!
线程的死锁
基本介绍
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生
释放锁
1.当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来
2.当前线程在同步代码块、同步方法中遇到break、return。
案例:没有正常结束,叫他修改bug,不得已出来
3.当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
案例:进去之后,发现忘带纸,不得已出来
4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
案例:吃饭付款时忘记带钱了,回去取钱,在来吃饭
下面操作不会释放锁
1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
案例:写作业 写的过程中突然睡着了
2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起该线程不会释放锁。
案例:正在打代码 有人叫出去有事 但是电脑没有关机
提示:应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用
本章笔记是观看韩顺平的JAVA的视频和在网上找的资料 以及自己的理解总结出来的笔记希望可以帮助大家,感谢大家的耐心观看 如有错误请即使联系我 我会及时修正