java基础知识复习

多线程

多线程的创建,方式一:继承于Thread类

1.创建一个继承于Thread类的子类
2.重写Thread类的run()-->将此线程执行的操作声明在run()中
3.创建Thread类的子类的对象
4.通过此对象调用start()

创建多线程的方式二:实现Runnable接口

1.创建一个实现了Runnable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()

比较创建线程的两种方式。

开发中:优先选择:实现Runnable接口的方式
原因:1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。

联系:publicclassThreadimplementsRunnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

同步机制,来解决线程的安全问题。

方式一:同步代码块

synchronized(同步监视器){
需要被同步的代码
}

说明:1.操作共享数据的代码,即为需要被同步的代码。-->不能包含代码多了,也不能包含代码少了。
2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁。

补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

方式二:同步方法。
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

5.同步的方式,解决了线程的安全问题。---好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。---局限性

方式三:Lock锁---JDK5.0新增

1.面试题:synchronized与Lock的异同?
相同:二者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())

2.优先使用顺序:
Lock》同步代码块(已经进入了方法体,分配了相应资源)》同步方法(在方法体之外)

死锁问题

1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

2.说明:
1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
2)我们使用同步时,要避免出现死锁。

常用的类

日期

jdk8之前的日期时间的API测试
1.System类中currentTimeMillis();
2.java.util.Date和子类java.sql.Date
3.SimpleDateFormat
4.Calendar
jdk8
LocalDate、LocalTime、LocalDateTime的使用
说明:
1.LocalDateTime相较于LocalDate、LocalTime,使用频率要高
2.类似于Calendar

java.util.Date类
|---java.sql.Date类

1.两个构造器的使用

构造器一:Date():创建一个对应当前时间的Date对象
构造器二:创建指定毫秒数的Date对象
2.两个方法的使用
toString():显示当前的年、月、日、时、分、秒
getTime():获取当前Date对象对应的毫秒数。(时间戳)

3.java.sql.Date对应着数据库中的日期类型的变量

如何实例化
如何将java.util.Date对象转换为java.sql.Date对象

string操作

对比String、StringBuffer、StringBuilder三者的效率:
从高到低排列:StringBuilder>StringBuffer>String

String、StringBuffer、StringBuilder三者的异同?
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储

指导意义:开发中建议大家使用:StringBuffer(intcapacity)或StringBuilder(intcapacity)

对象的比较

一、说明:Java中的对象,正常情况下,只能进行比较:==或!=。不能使用>或<的
但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
如何实现?使用两个接口中的任何一个:Comparable或Comparator

二、Comparable接口与Comparator的使用的对比:
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。

Comparable接口的使用举例:自然排序
1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列
3.重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。
4.对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。
在compareTo(obj)方法中指明如何排序
重写compare(Objecto1,Objecto2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。

其他常用类的使用

1.System
2.Math
3.BigInteger和BigDecimal

枚举类

枚举类的使用
1.枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类
2.当需要定义一组常量时,强烈建议使用枚举类
3.如果枚举类中只有一个对象,则可以作为单例模式的实现方式。

Enum类中的常用方法:
values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
valueOf(Stringstr):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
toString():返回当前枚举类对象常量的名称

使用enum关键字定义的枚举类实现接口的情况
情况一:实现接口,在enum类中实现抽象方法
情况二:让枚举类的对象分别实现接口中的抽象方法

List接口

1.List接口框架

|----Collection接口:单列集合,用来存储一个一个的对象
	|----List接口:存储有序的、可重复的数据。-->“动态”数组,替换原有的数组
		|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[]elementData存储
		|----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
		|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[]elementData存储

2.ArrayList的源码分析:
jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

3.LinkedList的源码分析:
LinkedListlist=newLinkedList();内部声明了Node类型的first和last属性,默认值为null
list.add(123);将123封装到Node中,创建了Node对象。

4.Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。

面试题:ArrayList、LinkedList、Vector三者的异同?
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据

List接口中的常用方法

总结:常用方法
增:add(Objectobj)
删:remove(intindex)remove(Objectobj)
改:set(intindex,Objectele)
查:get(intindex)
插:add(intindex,Objectele)
长度:size()
遍历:①Iterator迭代器方式
②增强for循环
③普通的循环

Set接口的框架:

|----Collection接口:单列集合,用来存储一个一个的对象
	|----Set接口:存储无序的、不可重复的数据-->高中讲的“集合”
		|----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
		|----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,	
			|----LinkedHashSet效率高于HashSet.
		|----TreeSet:可以按照添加对象的指定属性,进行排序。

1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。

2.要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值。

一、Set:存储无序的、不可重复的数据

以HashSet为例说明:
1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

2.不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。

二、添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。--->情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。--->情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。--->情况2

对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
jdk7:元素a放到数组中,指向原来的元素。
jdk8:原来的元素在数组中,指向元素a
总结:七上八下

HashSet底层:数组+链表的结构。

LinkedHashSet的使用
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个
数据和后一个数据。
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

1.向TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)

3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
4.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

Map的实现类的结构:

|----Map:双列数据,存储key-value对的数据---类似于高中的函数:y=f(x)
	|----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
		|----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
		原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
		对于频繁的遍历操作,此类执行效率高于HashMap。
	|----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用红黑树
	|----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
	|----Properties:常用来处理配置文件。key和value都是String类型

HashMap的底层:数组+链表(jdk7及之前)
数组+链表+红黑树(jdk8)

面试题:
1.HashMap的底层实现原理?
2.HashMap和Hashtable的异同?
3.CurrentHashMap与Hashtable的异同?(暂时不讲)

二、Map结构的理解:
Map中的key:无序的、不可重复的,使用Set存储所有的key--->key所在的类要重写equals()和hashCode()(以HashMap为例)
Map中的value:无序的、可重复的,使用Collection存储所有的value--->value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所有的entry

三、HashMap的底层实现原理?以jdk7为例说明:
HashMapmap=newHashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[]table。
...可能已经执行过多次put...
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据
的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
如果equals()返回false:此时key1-value1添加成功。----情况3
如果equals()返回true:使用value1替换value2。

补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。

在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

jdk8相较于jdk7在底层实现方面的不同:
1.newHashMap():底层没有创建一个长度为16的数组
2.jdk8底层的数组是:Node[],而非Entry[]
3.首次调用put()方法时,底层创建长度为16的数组
4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
4.1形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
4.2当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所数据改为使用红黑树存储。

DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
threshold:扩容的临界值,=容量填充因子:160.75=>12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

四、LinkedHashMap的底层实现原理(了解)
源码中:
staticclassEntry<K,V>extendsHashMap.Node<K,V>{
Entry<K,V>before,after;能够记录添加的元素的先后顺序
Entry(inthash,Kkey,Vvalue,Node<K,V>next){
super(hash,key,value,next);
}
}

五、Map中定义的方法:

添加、删除、修改操作:
Objectput(Objectkey,Objectvalue):将指定key-value添加到(或修改)当前map对象中
voidputAll(Mapm):将m中的所有key-value对存放到当前map中
Objectremove(Objectkey):移除指定key的key-value对,并返回value
voidclear():清空当前map中的所有数据
元素查询的操作:
Objectget(Objectkey):获取指定key对应的value
booleancontainsKey(Objectkey):是否包含指定的key
booleancontainsValue(Objectvalue):是否包含指定的value
intsize():返回map中key-value对的个数
booleanisEmpty():判断当前map是否为空
booleanequals(Objectobj):判断当前map和参数对象obj是否相等
元视图操作的方法:
SetkeySet():返回所有key构成的Set集合
Collectionvalues():返回所有value构成的Collection集合
SetentrySet():返回所有key-value对构成的Set集合

总结:常用方法:
添加:put(Objectkey,Objectvalue)
删除:remove(Objectkey)
修改:put(Objectkey,Objectvalue)
查询:get(Objectkey)
长度:size()
遍历:keySet()values()entrySet()

Collections:操作Collection、Map的工具类

面试题:Collection和Collections的区别?
reverse(List):反转List中元素的顺序
shuffle(List):对List集合元素进行随机排序
sort(List):根据元素的自然顺序对指定List集合元素按升序排序
sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
swap(List,int,int):将指定list集合中的i处元素和j处元素进行交换

Objectmax(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Objectmax(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
Objectmin(Collection)
Objectmin(Collection,Comparator)
intfrequency(Collection,Object):返回指定集合中指定元素的出现次数
voidcopy(Listdest,Listsrc):将src中的内容复制到dest中
booleanreplaceAll(Listlist,ObjectoldVal,ObjectnewVal):使用新值替换List对象的所有旧值

泛型的使用

1.jdk5.0新增的特性

2.在集合中使用泛型:
总结:
①集合接口或集合类在jdk5.0时都修改为带泛型的结构。
②在实例化集合类时,可以指明具体的泛型类型
③指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
比如:add(Ee)--->实例化以后:add(Integere)
④注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
⑤如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。

3.如何自定义泛型结构:泛型类、泛型接口;泛型方法。见GenericTest1.java

1.泛型在继承方面的体现

虽然类A是类B的父类,但是G <A>和G <B> 二者不具备子父类关系,二者是并列关系。

补充:类A是类B的父类,A<G>是B<G>的父类

流的分类:

1.操作数据单位:字节流、字符流
2.数据的流向:输入流、输出流
3.流的角色:节点流、处理流

二、流的体系结构
抽象基类节点流(或文件流)缓冲流(处理流的一种)
InputStreamFileInputStream(read(byte[]buffer))BufferedInputStream(read(byte[]buffer))
OutputStreamFileOutputStream(write(byte[]buffer,0,len)BufferedOutputStream(write(byte[]buffer,0,len)flush()
ReaderFileReader(read(char[]cbuf))BufferedReader(read(char[]cbuf)readLine())
WriterFileWriter(write(char[]cbuf,0,len)BufferedWriter(write(char[]cbuf,0,len)flush()

处理流之一:缓冲流的使用

1.缓冲流:
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
2.作用:提供流的读取、写入的速度
提高读写速度的原因:内部提供了一个缓冲区
3.处理流,就是“套接”在已有的流的基础上。
测试FileInputStream和FileOutputStream的使用

结论:
1.对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
2.对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理
处理流之二:转换流的使用
1.转换流:属于字符流
InputStreamReader:将一个字节的输入流转换为字符的输入流
OutputStreamWriter:将一个字符的输出流转换为字节的输出流

2.作用:提供字节流与字符流之间的转换

3.解码:字节、字节数组--->字符数组、字符串
编码:字符数组、字符串--->字节、字节数组

4.字符集
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。
GB2312:中国的中文编码表。最多两个字节编码所有字符
GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

其他流的使用
1.标准的输入、输出流
2.打印流
3.数据流

1.标准的输入、输出流
1.1
System.in:标准的输入流,默认从键盘输入
System.out:标准的输出流,默认从控制台输出
1.2
System类的setIn(InputStreamis)setOut(PrintStreamps)方式重新指定输入和输出的流。

1.3练习:
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,
直至当输入“e”或者“exit”时,退出程序。

方法一:使用Scanner实现,调用next()返回一个字符串
方法二:使用System.in实现。System.in--->转换流--->BufferedReader的readLine()

2.打印流:PrintStream和PrintWriter

2.1提供了一系列重载的print()和println()
2.2练习:

3.数据流
3.1DataInputStream和DataOutputStream
3.2作用:用于读取或写出基本数据类型的变量或字符串

练习:将内存中的字符串、基本数据类型的变量写出到文件中。

注意:处理异常的话,仍然应该使用try-catch-finally.

1.jdk7.0时,引入了Path、Paths、Files三个类。
2.此三个类声明在:java.nio.file包下。
3.Path可以看做是java.io.File类的升级版本。也可以表示文件或文件目录,与平台无关

4.如何实例化Path:使用Paths. staticPathget(Stringfirst,String…more):用于将多个字符串串连成路径 staticPathget(URIuri):返回指定uri对应的Path路径

对象流的使用
1.ObjectInputStream和ObjectOutputStream
2.作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

3.要想一个java对象是可序列化的,需要满足相应的要求。见Person.java

4.序列化机制:
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种
二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

网络编程

一、网络编程中有两个主要的问题:
1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
2.找到主机后如何可靠高效地进行数据传输

二、网络编程中的两个要素:
1.对应问题一:IP和端口号
2.对应问题二:提供网络通信协议:TCPIP参考模型(应用层、传输层、网络层、物理+数据链路层)

三、通信要素一:IP和端口号

1.IP:唯一的标识Internet上的计算机(通信实体)
2.在Java中使用InetAddress类代表IP
3.IP分类:IPv4和IPv6;万维网和局域网
4.域名:www.baidu.comwww.mi.comwww.sina.comwww.jd.com
www.vip.com
5.本地回路地址:127.0.0.1对应着:localhost

6.如何实例化InetAddress:两个方法:getByName(Stringhost)、getLocalHost()
两个常用方法:getHostName()getHostAddress()

7.端口号:正在计算机上运行的进程。
要求:不同的进程有不同的端口号
范围:被规定为一个16位的整数0~65535。

8.端口号与IP地址的组合得出一个网络套接字:Socket

反射之后,对于Person的操作

@Test
public void test2() throws Exception{
	Classclazz=Person.class;
	1.通过反射,创建Person类的对象
	Constructorcons=clazz.getConstructor(String.class,int.class);
	Objectobj=cons.newInstance("Tom",12);
	Personp=(Person)obj;
	System.out.println(p.toString());
	2.通过反射,调用对象指定的属性、方法
	调用属性
	Fieldage=clazz.getDeclaredField("age");
	age.set(p,10);
	System.out.println(p.toString());
	
	调用方法
	Methodshow=clazz.getDeclaredMethod("show");
	show.invoke(p);
	
	System.out.println("");

	通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
	调用私有的构造器
	Constructorcons1=clazz.getDeclaredConstructor(String.class);
	cons1.setAccessible(true);
	Personp1=(Person)cons1.newInstance("Jerry");
	System.out.println(p1);
	
	调用私有的属性
	Fieldname=clazz.getDeclaredField("name");
	name.setAccessible(true);
	name.set(p1,"HanMeimei");
	System.out.println(p1);
	
	调用私有的方法
	MethodshowNation=clazz.getDeclaredMethod("showNation",String.class);
	showNation.setAccessible(true);
	Stringnation=(String)showNation.invoke(p1,"中国");相当于Stringnation=p1.showNation("中国")
	System.out.println(nation);


}

疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
建议:直接new的方式。
什么时候会使用:反射的方式。反射的特征:动态性
疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
不矛盾。

关于java.lang.Class类的理解
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件
加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此
运行时类,就作为Class的一个实例。

2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式
来获取此运行时类。

获取Class的实例的方式(前三种方式需要掌握)

@Test
public void test3() throws ClassNotFoundException{
	方式一:调用运行时类的属性:.class
	Class clazz1=Person.class;
	System.out.println(clazz1);
	方式二:通过运行时类的对象,调用getClass()
	Person p1 = new Person();
	Class clazz2=p1.getClass();
	System.out.println(clazz2);
	
	方式三:调用Class的静态方法:forName(StringclassPath)
	Class clazz3= Class.forName("com.atguigu.java.Person");
	clazz3=Class.forName("java.lang.String");
	System.out.println(clazz3);
	
	System.out.println(clazz1==clazz2);
	System.out.println(clazz1==clazz3);
	
	方式四:使用类的加载器:ClassLoader(了解)
	ClassLoaderclassLoader=ReflectionTest.class.getClassLoader();
	Classclazz4=classLoader.loadClass("com.atguigu.java.Person");
	System.out.println(clazz4);
	
	System.out.println(clazz1==clazz4);

}

Lambda表达式的使用

1.举例:(o1,o2)->Integer.compare(o1,o2);
2.格式:
->:lambda操作符或箭头操作符
->左边:lambda形参列表(其实就是接口中的抽象方法的形参列表)
->右边:lambda体(其实就是重写的抽象方法的方法体)

3.Lambda表达式的使用:(分为6种情况介绍)

总结:
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略
->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字

4.Lambda表达式的本质:作为函数式接口的实例

5.如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用@FunctionalInterface注解,
这样做可以检查它是否是一个函数式接口。

6.所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

原文地址:https://www.cnblogs.com/sentangle/p/12401013.html