• 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
Copyright © 2020-2023  润新知