JAVA SE 基础学习笔记(二)

本笔记为初学Java基础时的笔记,年代久远,可参考性不大。
本笔记为初学Java基础时的笔记,年代久远,可参考性不大。
本笔记为初学Java基础时的笔记,年代久远,可参考性不大。
本笔记为初学Java基础时的笔记,年代久远,可参考性不大。
本笔记为初学Java基础时的笔记,年代久远,可参考性不大。


注意:在子类方法中,不管写不写super,java都会默认使用super调用父类的无参构造方

法。

PS:也就是说,子类会先去加载父类的构造方法,然后再去加载子类本身的构造方法。

1 )用在子类的构造方法中。

2 )不管写不写super,java都会默认使用super调用父类的构造方法。

3 )子类加载构造方法之前会先加载父类的构造方法,且只能调用父类构造方法其中一个的

一次。

4 )通过super调用父类构造方法时,必须放在子类构造方法的第一行。

1 )先父类对象再子类对象,先父类构造方法,再子类构造方法。

1.静态资源(static声明的资源)> new > {代码块} > 构造方法

2.同为静态资源,则按顺序执行。

3.静态代码块只会执行一次。

4.静态代码块不能访问实例变量和实例方法。(因为静态代码块比实例对象优先加载)

11.(super,static,重写)

1.super关键字

1.1定义:表示当前类的父类对象,可以使用super关键字实现对父类变量和方法的

访问。

1.2特点:

1.3创建一个子类对象的顺序:

3.static关键字

3.1定义:静态的,全局的。被static修饰的方法或者变量可以直接通过类名调用,

不需要实例化。一个类,共用同一个静态资源。

3.2JVM的类加载顺序:

3.3特点:

1 )static声明的资源只能被初始化一次,在编译通过之后,在开始运行之前被初始化。

2 )局部变量(方法中的变量)不能被static修饰

3 )静态方法只能调用其他静态方法,不能调用非静态变量和方法,不能使用this和super,

因为静态资源的加载在实例化之前。

4 )被static修饰的变量和方法独立于该类的任何对象,不依赖类的示例,且被该类的所有示

例共享。

5 )修饰内部类时,外部类不需要实例化,可以直接通过外部类名调用。*(不会用到)

3.4加载顺序例题(很重要)

1 public class Bus {
2
3 public static Bus b1 = new Bus();
4 public static Bus b2 = new Bus();
5 {
6 System.out.println("普通代码块");
7 }
8 static {
9 System.out.println("静态代码块");
10 }
11 public Bus() {
12 System.out.println("构造方法");
13 }
14 public static void main(String[] args) {
15 Bus bus = new Bus();
16 }
17 }
18 //该代码执行过程:
19 //1.public static Bus b1 = new Bus();
20 //该语句为static修饰的Bus类的实例化,则开始去执行bus中的代码
21 out:普通代码块
22 静态代码块不会执行,因为该静态资源在第一行静态资源之后,还没有被加载。
23 out:构造方法
24 main方法不会执行,也是静态资源。
25 //2.b2同上
26 out:普通代码块
27 out:构造方法
28 //3.此时,b1b2两个静态资源加载完毕,继续加载后续的static资源,即静态代码块

该例题图解

例题解读:

如果一个静态资源加载指向了既包括静态又包括非静态的代码块,则只能执行代码块中的非

静态资源,

因为此时根据静态资源的加载顺序,该代码块中的静态资源还未被加载,所以此时会执行非

静态资源。

本例题中,

public static Bus b1 = new Bus();

是实例化了bus类,所以会正常执行bus中的代码,但是其中的静态资源还没有被加载,所以

只会正常执行非静态资源代码。

3.1定义:在子父类中,如果子类的方法名、参数列表和返回值都与父类中的一个方法相同,则会发生重
写,重写后,再调用该方法,会根据就近原则调用子类的

3.1重写的特点

1 )重写发生在子父类中

2 )当方法名,参数列表,返回值类型都相同时,会发生重写

3 )重写的方法体不同(如果相同,那就没有必要重写)

4 )子类重写的方法,访问权限不能缩小。可以放大。

发生重载的方法之间是并列关系,后期可以根据参数列表的不同,分别调用。

29 out:静态代码块
30 //4.继续执行static主入口的代码
//5.因为此时static资源已经全部加载完成,且static资源只会执行一次,所以,现在main里的实例化bus,
并不会再执行static资源
31
32 out:普通代码块
33 out:构造方法
34 答案:
35 普通代码块 √
36 构造方法√
37 普通代码块√
38 构造方法√
39 静态代码块√
40 普通代码块√
41 构造方法√

3.重写

但是发生重写的两个方法,是新方法覆盖旧方法,只能调用新方法,旧方法已经被覆盖,在

子类中只能调用之类中重写之后的方法。

1 )修饰类时,类不能被继承。

2 )修饰方法,方法不能被重写。

3 )修饰变量,称为常量,常量的值不能被修改。(常量名必须大写)

(要么在声明的同时给常量赋值,要么在构造方法中完成初始化。)

3.1)修饰基本数据类型,被修饰对象的值不能改变。

3.2)修饰引用类型,被修饰对象的的内存地址不能改变。

String的本质是什么?

一个由final修饰的char类型的数组。

12.(final,abst,接口,多态)

1.final关键字:

1.1定义:表述为,最终的,不可更改的,修饰类,方法,和变量

1.2:特点:

1 final int Abc = 12;
2 //后面a的值无法再改变
3 final String Abc = "acj"
//后面无法再改变,因为修饰引用类型时,对象的内存地址不能改变,不管是通过“=”还是new String的方
式,
4
5 //都会导致Abc指向的内存地址改变,所以String不会再变。
6 final StringBuffer Abc = new StringBuffer("abc");
7 //由于stringbuffer的特性,更改其保存的值时,内存地址不会改变,
8 //所以,该种方式创建的值可以通过appe改变。符合3.2的要求
9

小TIPS:

1 )修饰类,类为抽象类,抽象类不能被实例化。他的作用是提供给其他类进行继承。

2 )修饰方法,方法为抽象方法,只有方法的签名(就是只有一行声明方法的代码),没有

方法体。

1 )抽象类不能实例化,抽象类不能创建对象。

2 )子类可以继承抽象类,但是子类必须重写抽象类中的所有抽象方法。

3 )抽象类,可以包含抽象方法和非抽象方法。(只比普通类多了抽象方法。)

4 ) 如果一个类包含抽象方法,那么该类必须声明为抽象类,否则将出现编译错误。 (抽象

方法只能存在于抽象类或接口中)

5 )抽象类可以有父类,但是父类必须有一个无参的构造函数,否则,抽象类必须声明一个

与父类相同的有参构造方法。

6 )抽象类不能被final修饰

2.abstract关键字:

2.1定义:抽象的。(主要用于框架)

1 public abstract void test(); //这就是签名

2.2特点:

1 父类:
2 public class Taxi {
3 public Taxi(String name) {
4 }
5 //子类是抽象类:
6 public abstract class Car extends Taxi{
7 public Car(String name) {
8 super(name);
9 }
10 }
11

2.3抽象类新建方法:新建类时勾选abstract即可

声明接口关键字是 interface

实现接口关键字是implements

1 ) 接口只有方法的签名和静态常量

2 )接口的 变量 ,会默认使用 public final static 修饰

3 )接口的 签名方法 。会默认使用 public abstract 修饰, 没有final,如果有final就不能被继承

4 )接口自身不能实例化,接口的实现类可以实例化

5 )子类实现接口,必须重写接口的所有方法。

6 )一个类可以实现多个接口,但是只能继承一个父类,且必须先继承后实现、

“先继承后实现,单继承多实现”

1 )解决java中的单亲继承问题。

2 )接口可以并行开发

3 )便于重构

3.接口

3.1定义:接口是方法签名的集合 。

1 public interface IPerson {
2 String s1 = "sadas"; //静态常量
3 void speak();
4 String fun1();
5 int fun2();
6 }
7 //2)接口的变量,会默认使用public final static 修饰
8 //3)接口的签名方法。会默认使用public abstract修饰
9

3.2接口的特点:

3.3接口的优点

3.4接口创建方式,new - interface。命名使用匈牙利命名法,例如 IPerson ICar

格式:父类 对象名 = new 子类()

即:父类引用(内存地址)指向子类对象。

1 )在java中,有两种形式可以实现多态,继承和接口。

2 )方法的重载实现的是编译时的多态性,而方法的重写实现的是运行时的多态性。

3 )本质还是new的父类,只是当子类重写了父类的方法时,会调用子类重写之后的方法。

即:父类型的对象,调用父类的方法时,如果方法在子类中重写,则调用的是子类重写的方

法。

4 )子类中定义的方法,无法通过父类型的对象调用。

6.1 抽象类和接口的相同与不同之处?

1 )抽象类和接口都不可被实例化,但是都可以被继承。

2 )子类若是继承抽象类,必须重写抽象类中的所有抽象方法,子类若是实现接口,必须重

写接口中的所有方法。

3 )子类只能继承一个抽象类,但是可以实现多个接口。

4.多态

4.1定义:是java语言中的一种,在运行时体现出多种形态的概念, 该概念主要体

现在下列情况:

4.2特点:

5.转型*

5.1向上转型:Father s = new Son(); 使用子类实例化一个父类。

5.2向下转型*:只有Object对象,有向下转型的可能。

Son f = (Son) new Father();

6.本节相关问题:

1.6Math.round() ,将小数四舍五入为整数

13.(Math,Date,Arrays,包装类)

1.Math类

1.1.Math.max( a,b) 返回ab中的大值

1.2.Math.min( a,b) 返回ab中的小值

1.3.Math.abs(a) 返回a的绝对值

1.4.Math.PI 返回圆周率

1.5.Math.pow(a,b) 返回a的b次方 Math.pow(10,2) = 10^

2.Date和LocalDate类

2.1Date

2.1.1新建Date

1 import java.util.Date;
2
3 Date d = new Date();
4 ///Sat Oct 15 10:11:42 CST 2022

2.1.2格式化Date

1 SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss SSS");
2 String dateString = sf.format(date); //格式化后,返回的是字符串
3 System.out.println(dateString);
4 //2022-10-15 10-21-33 266

2.1.3取 13 位时间戳

1 Long timestamp = date.getTime();
2 System.out.println(timestamp);
3 //

2.2.LocalDateTime

2.2.1 新建LocalDateTime

1 LocalDateTime lTime = LocalDateTime.now();
2 System.out.println(lTime);
3 //2022-10-15T10:30:23.

2.2.2LocalDT所属方法

2.2.3自定义日期,时间

1 //自定义日期
2 LocalDate date = LocalDate.of(2021, Month.MARCH, 25); //此处Month.MARCH为系统自带常量
3 System.out.println(date);
4 //自定义时间
5 LocalTime time= LocalTime.of(20, 14, 14);
6 System.out.println(time);

3.Arrays类(该类的方法直接改变原数组)

3.1 Arrays.fill(数组,值) 给该数组所有位置都赋值为 该值

3.2 Arrays.fill(数组,起始下标,结束下标,值) 给该数组,从开始下标到结束

下标之间的赋值为 该值。

3.3 Arrays.sort(数组) 给数组排序,默认升序。

1 int[] a = {1,5,6,9,4,7,8};
2 Arrays.sort(a);
3 System.out.println(Arrays.toString(a));
4 //[1, 4, 5, 6, 7, 8, 9]

3.4 Arrays.binarySearch(排序过的数组,值) 二分法查找该值在数组中的下

标,返回下标。必须使用排序过的数组。

1 int index = Arrays.binarySearch(a, 8);
2 System.out.println(index);
3 //

3.5 Arrays.copyOf(原数组,新数组长度) 复制原数组到长度为指定长度的新数

组,返回新数组。

1 int[] b = Arrays.copyOf(a, 12);
2 System.out.println(Arrays.toString(b));
3 //[1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0]

3.6 Array.equals(数组 1 ,数组2) 比较数组 1 和数组 2 中的长度,元素,元素顺序是

否全等,全等返回true

1 int[] a = {1,5,6,9,4,7,8};
2 int[] b = [1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0];
3 boolean res = Arrays.equals(a, b);
4 System.out.println(res);
5 //false

byte - > Byte || short - > Short || int - > Integer || long - > Long

float - > Float || double - > Double || char - > Character || boolean - > Boolean

装箱:把一个基本数据类型赋值给包装类

拆箱:把一个包装类赋值给基本数据类型

原理:integer会缓存[-128,127]这个范围的值,当参数属于这个范围时,会直接返回范围中的数字,超
出时,会创建新的integer对象,再返回该对象。
4.2.4 Integer.compare(int x,int y) 比较x和y的大小,返回int的 1 ( x > y ),-1( x < y ),0( x = y

4.包装类(既有值,又有方法)

4.1 8种基本数据类型对应的包装类:

4.2 Integer相关方法:(此处只拿Integer举例,其他类型一样有其所属方法。)

4.2.1 Integer.valueOf(其他类型) 将其他类型转integer类型

1 String aString = "123";
2 Integer bInteger = Integer.valueOf(aString);
3 System.out.println(bInteger instanceof Integer);
4 //true

4.2.2 Integer.toString(Integer类型); 将Integer类型转换为字符型

1
2 Integer a = 123;
3 String a2 = Integer.toString(a);
4 System.out.println(a2 instanceof String);
5 //true

4.2.3 Integer.MAX_VALUE Integer.MIN_VALUE 返回integer能存储的最大值和最小值。

1 int a = Integer.compare(23, 23);
2 System.out.println(a);
3 // 0
1 )集合的实现类中只能存放引用类型,不能存放基本数据类型,可以存储基本数据类型的包装类。
2 )集合接口有三种子类型,List,Queue,Set。
ArrayList是实现了基于动态数组的数据结构,在未声明长度的情况下,默认长度是 10 ,数据超出会自动
扩容原长度的50%。
ArrayList的相关方法:

注意:list的 add方法 ,如果原下标位置有元素,则会把原位置之后的所有元素后移一位,再

在该位置添加元素。 是添加,不是覆盖。

list的 set方法 ,是修改指定下标位置的值, 是覆盖 。

14.(List,Set,Qu,Map)

1.集合:

1.1集合相关概念:

1.2 List集合:可以重复,有序

1.2.1 ArrayList

1 .add(元素) 添加元素 .add(index,元素) 指定下标添加元素
2 .set(index,元素) 指定下标修改元素
3 .get() 获取元素
4 .size()获取集合长度,返回int类型
5 .remove(index) 根据下标,删除指定下标位置的元素,该元素之后的所有元素前移一位
6 .remove(元素) 根据元素,删除list中第一次出现的指定元素,该元素之后的所有元素前移一位。
7 syso(list) 打印list中所有元素
8 .contains(元素) 返回是否包含该元素的布尔值。

Linklist 是基于双向链表的数据结构。

LinkedList方法同ArrayList的相关方法

2 )Linklist 是基于双向链表的数据结构。

3 )ArrayList在尾端插入和访问

数据时效率高于LinkedList,因为LinkdList要查找指针。

4 )LinkedList对于中间插入或者头部插入时效率高于ArrayList,因为ArrayList要移动数据。

两者添加数据的示意图:

1 基础动态数组的数据结构;
2 是线程同步的,执行效率慢。
3 数据填充时会自动扩充原长度的100%。Arraylist更节省空间。
Vector方法同arraylist方法
set集合没有下标,输出时,默认升序输出。

1.2.2 LinkedList

1.2.3 两者区别?

1 )Arraylist是基于动态数组的数据结构,在未声明长度的情况下,默认长度是 10 ,数据

填满会自动扩充原来长度的50%。

1.2.4 Vector *

1.3 Set集合:不可重复,且无序。

1 .add()添加元素,无法添加set中重复的元素。

2 .size()获取集合长度,返回int类型

3 .contains(元素) 返回是否包含该元素的布尔值。

4 .remove(元素) 删除指定元素。

5 .clear()删除所有元素。

队列:先进先出 栈:后进先出
Queue队列相关方法:

2.队列和栈

2.1队列和栈基本概念

2.2Queue队列:实现类是LinkedList

1 Queue<Integer> qu = new LinkedList<>();

1 .add(元素) 添加元素,当插入元素超出容量长度时,add()会报异常

2 syso(qu) 打印输出qu所有元素

3 .size()获取队列长度,返回int类型

4 .element()获取但不删除此队列的头部元素,如果此队列为空,他会引发异常

1 Integer firstInteger1 = qu.element();
2 System.out.println(firstInteger1);

5 .peek()获取但不删除此队列的头部元素,如果此队列为空,他会返回null

1 Integer firstInteger2 = qu.peek();
2 System.out.println(firstInteger2);

6 .poll()获取并删除此队列的头部元素,如果此队列为空,他会返回null

目前queue的容量长度未知。
Map中存储的是 键值对(Key-Value) ;
HashMap是Map接口的实现类,该类根据键的哈希值存储数据。
1 )键值对,只能是引用类型的数据。
2 )Map的key不允许重复,多次给同一个key赋值,后面的会覆盖前面的值。
1 Integer firstInteger3 = qu.poll();
2 System.out.println(firstInteger3);

7 .offer(元素) 添加元素,当插入元素超出容量长度时,offer()会返回false,成功返回

true

8 遍历队列,遍历之后queue会变成空队列

1 Integer eleInteger = null;
2 while ((eleInteger = qu.poll()) != null) {
3 System.out.println(eleInteger);
4 }

3 Map集合

3.1 Map相关概念:

3.2 Map的特点:

3.3 Map相关方法:

1 Map<String, String> map = new HashMap<>();
Set< Entry<String,String> > eSet = map.entrySet();

1 .put(键,值)添加元素,键不能重复,重复put会覆盖。

1 map.put("k1", "张三");
2 map.put("k2", "李三");
3 map.put("k1", "王五");
4 System.out.println(map);
5 //结果:{k1=王五, k2=李三},键(key)是唯一的,不可重复的

2 .get(key)获取指定键的值

3 .size() 获取map集合的长度,即键/值对个数,返回int类型

4 .keySet() 获取集合中的所有键组成的Set集合

1 Set<String> keySet = map.keySet();
2 for(String k : keySet) {
3 System.out.println(map.get(k));
4 }
5 //.keySet() :返回一个由map中所有的key组成一个set集合。

5 .clear()清空所有元素。

6 .containsKey(key) 是否包含指定的key,返回布尔值。

7 .containsValue(value) 是否包含指定的value,返回布尔值。

8 .remove(key) 根据key去删键值对

9 .entrySet() 返回键值对的Set集合

1 import java.util.HashMap;
2 import java.util.Map;
3 import java.util.Map.Entry;53*
1 )HashMap底层是数组加链表的结构,Java8之后又加入了红黑树。
2 )当添加一个元素(key -value )时,首先计算key的哈希值,以此来确定插入到数组中的位置。
哈希算法的一种功能: 散列算法 ,此处是均匀的将键值对分布到数组的位置中。
3 )如果根据hash值确定的数组位置中已经存在元素,就添加到同一个hash值的元素的后面,形成了链
表。
4 )当链表长度大于 8 ,且数组中元素个数大于 64 时,链表转换为红黑树,以此提高查找效率。
5 )当红黑树中的节点个数小于 6 个,红黑树又重新转换为链表。
------以上关于红黑树的详细介绍见JAVA基础知识目录下笔记----- -
3.4 HashMap扩容因子 0.
当HashMap容量达到最大容量的75%时,会扩容原长度的两倍。
1. 开放定址法, 也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从hash表中找
到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中。ThreadLocal就用到了线性探测法
来解决hash冲突的。
2. 链式寻址法, 这是一种非常常见的方法,简单理解就是把存在hash冲突的key,以单向链表的方式来
存储, 比如HashMap就是采用链式寻址法来实现的。
4 import java.util.Set;
5 //Entry就是一个可以存储键值对的类型,表示一对键值对。
6
7 Map<String,String> map = new HashMap<>();
8 map.put("asd", "123");
9 map.put("asdd", "12342");
10
11 Set<Entry<String,String>> eSet = map.entrySet();
12 for (Entry<String, String> k : eSet) {
13 System.out.println(k.getKey() + ":" + k.getValue());
14 }
15 //asd : 123 asdd : 12342

3.4 HashMap的实现原理

3.4.1 结构:数组加链表,Java8之后又加入了红黑树。

3.4.2 运行过程:

3.5 如何解决hash碰撞

3. 再hash法 ,就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash函数对这个key做
hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大。
4. 建立公共溢出区 , 就是把hash表分为基本表和溢出表两个部分,凡事存在冲突的元素,一律放入到
溢出表中。
1-2-3-N

println(对象),是去调用对象的toString(),打印的是toString方法的结果。

如果对象是一个集合,就是集合中的每一个元素的toString方法。

Object类默认的toString方法为:

返回: 类名+内存地址

可以通过重写Object类的toString方法来获得自己想要的结果。

从左到右判断,如果没有出现字符型,则按整数运算,一旦遇到字符型,则字符型及之后的所有都是字
符拼接。

15.(枚举,异常,错误,print)

1 print运行原理

1.1 基本运行原理

1 public String toString() {
2 return getClass().getName() + "@" + Integer.toHexString(hashCode());
3 }
4

1.2 print打印带 “ +” 时的运行规则

1 System.out.println(1 + 2 + 50 + "*" + 1 + 3); //53*
2 System.out.println("" + 1 + 2 + 5); ///
3 System.out.println(1 + "" + 2 + 5 + 7); ///
1 )用enum关键字定义枚举类。 新建方法:右键 - new - Enum
2 )枚举类默认继承java.lang.Enum类
3 )枚举类的构造方法只能使用private修饰符,如果构造方法省略修饰符,则默认添加。
4 )枚举类的所有对象(示例),必须在枚举类中显式列出,否则这个枚举类将永远不能创建实例化对
象。
5 )枚举类列出的对象,系统会自动添加public static final修饰符;只是实例化之后的对象是自动添加,
不是变量,变量是任意的。
6 )类的对象是有限个,确定的。
代码示例:

2 枚举类

2.1 枚举的特点:

1 package demo2;
2
3 public enum EnumDemo {
4 e1(),e2("张三"),e3(),e4(); //显式列出
//枚举类的所有对象(示例),必须在枚举类中显式列出,否则这个枚举类将永远不能创建实例化对
象。
5
6
7 private EnumDemo() {
8
9 }
10 private EnumDemo(String name){
11 this.name = name;
12 }
13 //枚举类的构造方法只能使用private修饰符,如果构造方法省略修饰符,则默认添加。
14
15 /*****************set 和 get***********************/
16 public String getName() {
17 return name;
18 }
19
20 public void setName(String name) {
21 this.name = name;
22 }
23 }
24 ////////////////////////////使用方法//////////////////////
25 package demo2;
1 )用于存储不变的信息,例如账户,IP,秘钥等信息。
2 )存储一组常量。
为了避免无限创建类对象,节省内存空间。
懒汉式:节省 内存空间,存在并发问题
饿汉式:不存在并发问题
26
27
28 public class Demo1 {
29 public static void main(String[] args) {
EnumDemo e1 = EnumDemo.e1; ///直接将实例化之后的e1.e2赋值给新的EnumDemo类
变量。
30
31 e1.setName("张五");
32 System.out.println(e1.getName());
33 }
34 }
35
36

2.2 枚举类的使用场景

3 单例模式:一个类,有且只有一个对象。

3.1 写法一:枚举类只创建一个对象。

1 public enum Enm {
2 e1();
3 private Enm(){
4
5 }
6 }

3.2 写法二:懒汉式。先定义,到用时创建。

1 package demo2;
异常的继承关系
2
3 public class Person {
4
5 private static Person p; //先定义,不创建。
6 private Person() {}; //构造方法私有化,封死实例化途径
public static Person getPerson() { ///提供公有创建方法,如果p已创建直接返回p,没有
则创建。
7
8 if (p == null ) {
9 p = new Person();
10 }
11 return p;
12 }
13 }
14
15 //使用方法:
16 Person p = Person.getPerson();

3.3 写法三:饿汉式。先创建,到用时取用。

1 package demo2;
2
3 public class Person {
4
5 private static final Person p = new Person(); // 直接实例化
6 private Person() {}; //构造方法私有化,封死实例化途径
7 public static Person getPerson() { ///提供公有方法,来获取已经创建的p
8 return p;
9 }
10 }
11
12 //使用方法:
13 Person p = Person.getPerson();

4 异常

因为用户错误或其他外在操作错误,导致的代码无法继续运行,引起的异常。比如:权限不足、变量不
存在、处理的文件不存在等。
运行时异常是可能被程序员避免的异常,与检查性异常相反, 运行时异常可以在编译时被忽略 ,比如,
空指针异常,下标越界异常。
代码执行顺序:try(catch) > finally > return

4.1 检查性异常:(无法避免)

4.1 运行时异常:(可以避免)

4.3 捕获异常:

1 public static int show() {
2 try { //跟可能发生异常的代码
3 int[] a = new int[5];
4 System.out.println(a[5]); //下标越界
5 return 1;
6 } catch (Exception e) { //捕获异常,出现异常之后需要执行的代码
7 //Exception处可以填各种异常情况,例如
8 //catch (IndexOutOfBoundsExceptiong e) //捕获下标越界异常
9 System.out.println("出现异常");
10 e.printStackTrace(); //打印异常信息
11 return 2;
12 } finally {
13 //无论是否出现异常,final中的代码都会在最后运行
14 //如果try中有return,则先执行fina中的代码再返回数据。
15 System.out.println("finally");
16 }
17 }
18 //结果: 出现异常 异常信息 finally 2
19 //出现异常
20 //java.lang.ArrayIndexOutOfBoundsException: 5
21 // at demo2.Person.show(Person.java:9)
22 // at demo2.Person.main(Person.java:28)
23 //finally
24 //2
错误不是异常,而是脱离程序员控制的问题,错误在代码中通常会被忽略。
例如: java.lang.StackOverflowError 栈内存溢出错误 JVM内存溢出
错误在编译阶段也是检查不到的,也可以通过try{}catch{}捕获。
错误代码示例
1 )进程是执行中的应用程序
2 )一个进程可以包含一个或者多个线程
3 )一个进程至少要包含-一个线程(比如java中运行main方法的主线程)
1 )一个进程至少要包含-一个线程(比如java中运行main方法的主线程)

5 错误

1 public class Person {
2
3 static int count = 0;
4 //递归,在自己内调用自己
5 public static void fun1() {
6 count++;
7 System.out.println(count);
8 try {
9 fun1();
10 }catch (Exception e) {
11 e.printStackTrace();
12 }
13 }
14 public static void main(String[] args) {
15 fun1();
16 }
17 }

16.(多线程基础)

1 多线程相关概念

1.1 进程:

1.2 线程:

2 )线程是程序中的顺序控制流,只能使用分配给程序(进程)的资源和环境。
解释:
运行一个Java程序的实质是启动一个Java虚拟机进程,也就是说一个运行的Java程序
就是一个Java虚拟机进程。
线程是进程中可独立执行的最小的执行单元,一个进程中有多个线程,同一进程中的线
程共享该进程的资源(内存,空间,变量,方法)。
单线程:线程是进程中所要完成的一个计算过程,也被称为进程的任务
多线程:多线程:多线程是在一个程序中同时运行多个任务。
一个人,一直按顺序做事,总耗时是多个任务执行时间的总和。
多个人,同时做多个任务:总耗时取决于用时最长的任务
一个人,交替的做多个任务;
1 )继承Thread类: extends Thread
2 )实现runable接口:implements Runnable
3 )通过Callable接口和FutureTask对象创建线程
4 )通过线程池创建对象
调用start方法不是立即去调用run,而是先去争用CPU时间片,一旦获取CPU时间片,才会去调用run
方法。
CPU时间片:时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即
该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则
CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不

1.3 单/多线程

1.4 串行:

1.5 并行:

1.6 并发:

2 创建新线程

2.1 创建新线程相关概念

2.1.1 创建新线程的几种方法:

2.1.2 start()运行原理:

会造成CPU资源浪费。在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但
在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时
间片,每个程序轮流执行。

2.2 创建方法一 继承Thread类:extends Thread

1 public class Demo1 {
2 public static void main(String[] args) {
3 // 创建新线程
4 Th1 th1 = new Th1();
5 th1.setName("线程1111"); ///设置线程名字
6 // 启动新线程
7 th1.start();
8 }
9 }
10
11
12 class Th1 extends Thread{
13
14 @Override
15 public void run() {
16
17 String tNameString = Thread.currentThread().getName();
18 System.out.println("我的名字是:" + tNameString);
19 System.out.println("线程一已启动!");
20 }
21
22
23 }

2.3 创建方法二 实现runnable接口:implements Runnable

1 public class Demo1 {
2
3 public static void main(String[] args) {
4 //创建线程
5 Th2 t2 = new Th2();
1 )新建状态 (New) : 线程对象创建后,即进入新建状态。
2 )就绪状态 (Runnable) : 当 调用 线程对象的 start() 方法, 线程进入就绪状态 。处于
就绪状态的线程,只是说明该线程已经做好了准备,随时等待CPU调度执行,并不是
说执行了start()该线程就会立即执行;
3 )运行状态 (Running) : 当CPU开始调度处于就绪状态的线程时 ,此时线程才开始执
行( 执行run()方法 ),即 进入运行状态 。
注意,就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于
就绪状态中;
4 )阻塞状态 (Blocked) :处于运行状态中的线程由于某种原因, 暂时放弃对CPU的使用
权,停止执行,此时 进入阻塞状态 。直到其 重新进入到就绪状态 ,才有机会再次被CPU
调用以进入到运行状态。
根据阻塞产生的原因不同, 阻塞状态又可以分为 3 种 : I/O join wait sleep
1.等待阻塞: 运行状态中的线程执行object .wait()方法
2.同步阻塞: 线程在获取synchronized内部锁失败(因为锁被其它线程所占用)
3.其它阻塞: 通过调用线程的Thread.sleep()、join()方法或发出I/O请求时,线程会进入到阻塞状态。
当sleep()状态超时(sleep 休眠结束)、 join()等 待线程终止或超时、或者I/O处理完毕时,线程重新转入
就绪状态。
6 Thread t21 = new Thread(t2,"线程22222");
7 //启动线程
8 t21.start();
9
10 }
11 }
12
13 class Th2 implements Runnable{
14 @Override
15 public void run() {
16 String tNameString = Thread.currentThread().getName();
17 System.out.println(tNameString);
18 System.out.println("我是线程二");
19 }
20
21 }
22

3 线程的生命周期

3.1 五种状态:

5 )死亡状态 (Dead) :线程执行完毕或者因异常退出了run()方法,该线程结束生命周期,线程销毁。
(并不是立即销毁,后期讲垃圾回收)
1 )sleep属于Thread类,wait属于Object类
2 ) sleep方法 表示线程休眠指定的时间, 让出CPU资源,不让出锁 ,时间结束后,线程继续执行。
3 ) wait方法 表示线程等待,此时线程会 让出CPU资源,让出锁 ,直到其他线程通过notify()/notifyAll()方
法唤醒该线程,该线程才会继续执行。
1 )这种锁被称为监听器或内部锁
2 )内部锁是一种排它锁:被哪个线程拿到锁,该线程才能调用同步方法,其他线程将处于阻塞状态。
3 )内部锁是通过synchronized关键字实现的。
4 )synchronized可以用来修饰方法及代码块。
例如: static synchronized void add() {} ,则代表add方法不允许被同时调用,只能单独调用。

用synchronized修饰的方法就是同步方法

synchronized方法加锁对象是this,当synchronized需要在静态资源中修饰时,可以选择当前类作为锁
(类名.class)。

3.2 线程生命周期示意图

3.3 sleep与wait的区别*

4 线程同步

4.1 同步锁概念

4.2 synchronized关键字用法:

4.2.1 直接修饰方法本身:

1 public synchronized void add() {
2 for (int i = 0; i < 50000; i++) {
3 money++;
4 System.out.println(money);
5 }
6 }

4.2.2 修饰代码块:

synchronized ( lockObjec t){} ,( lockObjec t)是要选择一个共享实例作为锁。可以是类(类名.class),
可以是实例化之后的类对象(this),优先选择this作为锁。
区别就是在选择什么作为锁来使用。
1. 该例子中的方法为静态方法,不需要实例化,在静态方法中也不能使用this,此处把一个类当做锁。
1 public void add() {
2 synchronized (Demo1.class) { // 此处是把Demo1.class作为锁来用。
3 for (int i = 0; i < 100000; i++) {
4 count++;
5 System.out.println(count);
6 }
7 }
8 }

4.2.3 类锁与对象锁?(把一个类作为锁使用)?(把一个实例化之后的对象作为锁)

1 package demo2;
2 public class Demo1 {
3 static int count ;
4 // static synchronized void add() { ///写法一
5
6 static void add() {
7 synchronized (Demo1.class) { ///写法二
8 for (int i = 0; i < 100000; i++) {
9 count++;
10 System.out.println(count);
11 }
12 }
13
14 }
15
16 public static void main(String[] args) throws InterruptedException {
17 Th1 threTh1 = new Th1();
18 threTh1.start();
19 Th1 threTh2 = new Th1();
20 threTh2.start();
21 }
2. 该例子中的方法为普通方法,需要实例化之后使用。可以用this指实例化之后的对象,当做锁来用。
但是,也可以用类作为锁。
22 }
23
24
25 class Th1 extends Thread{
26
27 @Override
28 public void run() {
29 Demo1.add();
30 }
31
32 }
1 package demo2;
2 public class Demo1 {
3 int money;
4 public void add() {
synchronized (this) { ///此处,可以用this,也可以用Demo1.class,可以是任
意的共享示例
5
6 for (int i = 0; i < 50000; i++) {
7 money++;
8 System.out.println(money);
9 }
10 }
11 }
12 public static void main(String[] args) {
13 Demo1 demo1 =new Demo1();
14 Worker t1 = new Worker(demo1, "t11");
15 Worker t2 = new Worker(demo1, "t22");
16 t1.start();
17 t2.start();
18 }
19 }
20
21
22 class Worker extends Thread{
23
定义:如果一个类被设计为 允许多线程正确访问,我们就说这个类就是“线程安全”的(thread-safe)

Java标准库的java.lang. StringBuffer是线程安全的 。

还有一些不变类,例如 String,Integer,LocalDate , 它们的所有成员变量都是final ,多线

程同时访问时 只能读不能写 ,这些不变类也是线程安全的。

最后,类似Math这些只提供静态方法,没有成员变量的类,也是线程安全的。

除了上述几种少数情况,大部分类,例如ArrayList,都是非线程安全的类,我们不能在多线

程中修改它们。

但是,如果所有线程 都只读取,不写入 ,那么ArrayList是可以 安全地 在线程间共享的。

PS:没有特殊说明时,一个类默认是非线程安全的。

Java的线程锁是可重入的锁。
代码示例:
24 private Demo1 d;
25 public Worker(Demo1 d,String name) {
26 this.d = d;
27 this.setName(name);
28 }
29 @Override
30 public void run() {
31 d.add();
32 }
33
34 }

5 线程安全

6 死锁

6.1 可重入锁

1 public class Counter {
2 private int count = 0;
3 public synchronized void add(int n) {
4 if (n < 0) {
5 dec(-n);
6 } else {

观察synchronized修饰的add()方法,一旦线程执行到add()方法内部,说明它已经获取了当

前实例的this锁。

如果传入的n < 0,将在add()方法内部调用dec()方法。由于dec()方法也需要获取this锁,现

在问题来了:

对同一个线程,能否在获取到锁以后继续获取同一个锁?

答案是肯定的。

JVM允许同一个线程重复获取同一个锁,这种 能被同一个线程反复获取的锁 ,就叫做 可重入

锁。

死锁产生的条件是多线程各自持有不同的锁,并互相试图获取对方已持有的锁,导致无限等待;
1 ) 在获取多个锁的时候,不同线程获取多个不同对象的锁可能导致死锁。
2 )两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,造成了双方无限等待下去,这就是
死锁。
3 ) 死锁发生后 , 没有 任何机制 能解除死锁 ,只能 强制结束 JVM进程。
代码示例
7 count += n;
8 }
9 }
10 public synchronized void dec(int n) {
11 count += n;
12 }
13 }

6.2 死锁

1 public void add(int m) {
2 synchronized(lockA) { // 获得lockA的锁
3 this.value += m;
4 synchronized(lockB) { // 获得lockB的锁
5 this.another += m;
6 } // 释放lockB的锁
7 } // 释放lockA的锁
8 }
9
10 public void dec(int m) {
11 synchronized(lockB) { // 获得lockB的锁
12 this.another -= m;
4 ) 避免死锁的方法是多线程获取锁的顺序要一致。 例如:严格按照获取lockA再获取lockB的顺序。
数据在两个设备间的传输称为流,流是一组有顺序的,有起点和终点的字节集合。
I 是 input的缩写,表示输入流。
O 是 output的缩写,表示输出流。
根据数据流向的不同分为:
输入流: 数据由文件流向程序(读文件)客户端--->服务端
输出流: 数据由程序流向文件(写文件)服务端--->客户端
根据数据类型的不同分为:
字节流: 数据流中最小的数据单元是字节byte。(图片,视频)
字符流: 数据流中最小的数据单元是字符,JAVA中的字符是Unicode编码,一个字符占用两个字节。
(一般只用于处理文档类)
13 synchronized(lockA) { // 获得lockA的锁
14 this.value -= m;
15 } // 释放lockA的锁
16 } // 释放lockB的锁
17 }
18 //开始:
19 //线程 1 :进入add(),获得lockA;
20 //线程 2 :进入dec(),获得lockB。
21 //随后:
22 //线程 1 :准备获得lockB,失败,等待中;
23 //线程 2 :准备获得lockA,失败,等待中。
24 //此时,两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,
25 //造成了双方无限等待下去,这就是死锁。

17. (I/O流,File,复制,序列化)

1. 定义:

2. 分类:

3. 读写文件

字节流读写中,.flush()方法可被省略。

available(),返回输入流中可被获取的字节长度。当调用.read()方法时,.available()方法返回
的值会-1。

3.1 字节流读写

1 ///////////////////////读取/////////////////////
2 ////////////****读取英文***////////
3 // 新建File对象,加载文件物理路径
4 File file = new File("E:\\测试.txt");
5 // 创建字节输入流
6 InputStream in = new FileInputStream(file);
7 // 返回字节输入流的可读字节长度
8 int size = in.available();
9 // 输出读取到的数据
10 for (int i = 0; i < size; i++) {
11 char c = (char) in.read();
12 System.out.print(c);
13 }
14
15 ////////////********************读取中文*******************////////
16 byte[] bt = new byte[size];
17 for(int i = 0;i < bt.length;i++){
18 bt[i] = (byte) in.read();
19 }
20 syso(new String(bt));
21
22 ///////////////////////写入/////////////////////
23 //1 创建File类对象
24 File file = new File("E:\\测试.txt");
25
26 //2 定义输出内容
27 String string = "不想上班!!";
28
29 //3 创建输出流
30 OutputStream out = new FileOutputStream(file);
31

字符流读写中,.flush()方法不可被省略。

32 //上述方法为覆盖原值,可以使用
33 //OutputStream out = new FileOutputStream(file,true);
34 //实现继续在原有数据后拼接的效果。
35
36 //4 写入内容的字节数组到输出流
37 out.write(string.getBytes());
38
39 //5 刷新输出流(可以省略)
40 out.flush();
41
42 //6 关闭输出流
43 out.close();

3.3 字符流读写

1 ///////////////////////读取/////////////////////
2 // 新建File对象,加载文件物理路径
3 File file = new File("E:\\config.ini");
4 // 创建字节输入流
5 InputStream in = new FileInputStream(file);
6 // 对字节输入流进行编码,UTF-8国际标准码,GBK中文编码
7 InputStreamReader reader = new InputStreamReader(in,"UTF-8");
8 1// 创建字符流对象
9 BufferedReader bReader = new BufferedReader(reader);
10 // readline()读取文件中的一行数据
11 String string = null;
12 while ((string = bReader.readLine()) != null) {
13 System.out.println(string);
14 }
15
16 //关闭流资源
17 br.close();
18 reader.close();
19 in.close();
20
21 ///////////////////////写入/////////////////////
22 File file = new File("E:\\config.ini");
23 try {
24 OutputStream out = new FileOutputStream(file, true);
25 OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
26 BufferedWriter bw = new BufferedWriter(writer);
27 bw.write("!!!");
28 bw.newLine();
29 bw.write("asdasd");
30 bw.flush();
31 } catch (Exception e) {
32 e.printStackTrace();
33 }
34 bw.close();
35 writer.close();
36 out.close();

4. File类常用方法

1 File file = new File("E: \aaa. txt");
2 //此后所有的方法都基于指定的路径

1 ).exists() 判断指定的文件是否存在

1 boolean has = file.exists();

2 ).isFile() 判断指定的目录是否是一个文件

1 System.out.println(file.isFile());

3 ).isDirectory()判断指定的目录是否是一个文件夹

1 System.out.println(file.isDirectory());

4 ).list() 获取指定的目录下的所有文件名,返回一个所有文件名的String数组

1 String[] fileNames = file.list();
2 System.out.println(Arrays.toString(fileNames));

5 ).listFiles() 获取指定的目录下的所有文件对象,返回一个所有文件的File数组

1 File[] files = file.listFiles();

6 ). delete() 删除指定目录文件或者文件夹

1 boolean tar = file. delete();
2 System.out.println(tar);

7 ).deleteOnExit() 当文件或文件夹存在,则执行删除

1 file.deleteOnExit();

8 ).mkdir(),根据指定的文件路径,创建文件夹。成功返回true,存在同名文件夹

返回false。

1 boolean tar1 = file.mkdir();
2 System.out.println(tar1);
序列化 : 将对象转换成特定格式的字符串叫做序列化;
反序列化 : 将特定的字符串转换为对象叫做反序列化;
序列化实现接口java. io. Serializable

9 ).createNewFile() 根据指定的文件路径,创建一个新的文件。该方法可能会报

1 try {
2 boolean tar11 = file.createNewFile();
3 System. out. println(tar11);
4 } catch (AccessException e) {
5 e. printStackTrace( );
6 }

10 ).canRead()判断一个文件是否可读

11 ).canWrite()判断一个文件是否可写

5 复制大文件

1 public static void add() throws IOException {
2 InputStream in = new FileInputStream(new File("E:\\demo.mp4"));
3 OutputStream out = new FileOutputStream(new File("E:\\tar.mp4"));
4
5 byte[] b = new byte[1024];
6 int len;
7 while ((len = in.read(b)) != -1) {
8 out.write(b);
9 }
10 out.close();
11 }

6 序列化与反序列化

6.1 定义

1) 把对象的字节序列永久 地保存到硬盘上,通常存放在一个文件中;
2) 在网络 上传送对象的字节序列。
在类中必须用static修饰
序列化ID起着关键的作用,java的序列化机制是通过在运行时判断类的serialVersionUID来 验证版本一致
性 的。
反序列化时,JVM 会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,
如果相同则认为是一致的, 便可以进行反序列化,否则就会报序列化版本不-致的异常。

普通的自定义静态资源不参与序列化。但是 serialVersionUID参

与序列化。

重点是: ObjectOutputStream和ObjectIntputStream

6.2 对象的序列化的用途:

6.3 序列化id的作用 : 验证版本一致性

6.4 序列化代码示例:

1 package demo2;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.ObjectInputStream;
11 import java.io.ObjectOutputStream;
12 import java.io.OutputStream;
13 import java.io.Serializable;
14
15 public class Serial {
16
public static void main(String[] args) throws IOException,
ClassNotFoundException {
17
18
19 User u = new User();
20 u.setPass(40);
21 u.setNameString("张三");
22 //序列化,使用ObjectOutputStream类
23 File file = new File("E:\\测试.txt");
24 OutputStream out = new FileOutputStream(file);
25 ObjectOutputStream objOut = new ObjectOutputStream(out);
26 objOut.writeObject(u);
27
28 //反序列化
29 File file = new File("E:\\测试.txt");
30 InputStream in = new FileInputStream(file);
31 ObjectInputStream objIn = new ObjectInputStream(in);
32 User u2 = (User) objIn.readObject();
33 System.out.println(u2.getNameString());
34
35 }
36
37 }
38 class User implements Serializable{ ///被序列化的类必须实现接口
39 public static final long serialVersionUID = 1000000001L;
40
41 private String nameString;
42 private int pass;
43 public String getNameString() {
44 return nameString;
45 }
46 public void setNameString(String nameString) {
47 this.nameString = nameString;
48 }
49 public int getPass() {
50 return pass;
51 }
52 public void setPass(int pass) {
53 this.pass = pass;
54 }
55
56 }

18. (计算机网络,TCP)

Internet Protocol 互联网协议,为计算机网络相互连接进行通信而设计的协议。

IP地址具有唯一性,通过IP地址可以准确的定位一台计算机。

IP地址的范围: 0.0.0.0 - 255.255.255.255

本地计算机的IP地址:127.0.0.1 或 0.0.0.0

端口号包括 物理端口 和 逻辑端口 。

物理端口:是用于连接物理设备之间的接口(服务器在网路中的地址)

逻辑端口:是逻辑上用于区分应用程序(服务)的端口(应用程序在服务器上的地址)

端口号只有整数,范围从0-65535(2^16-1=65536)

系统端口号:0-1023(2^10=1024)

登记端口号:1024-49151,分配给其他应用程序的端口号

客户端口号:49152-65535,这类端口号在客户进程运行时才动态选择,通信结束,释放端

口号供其他客户进程使用。

TCP协议 :Transmission Control Protocol 传输控制协议

通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据

安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。

1 实现通信的条件

1.1 IP协议

1.2 IP地址

1.3 端口号

常见开发软件的默认端口

HTTP(网络通信) 80

FTP(文件传输) 21

TELNET() 23

Tomcat(Java项目容器) 8080

MySQL(数据库) 3306

reids(非关系型数据库) 6379

nacos(注册中心) 8848

2 TCP协议/IP协议

1) 应用层: 网络服务与最终用户的一个接口

协议有: HTTP FTP SMTP TFTP SNMP DNS TELNET HTTPS POP3 DHCP

2) 传输层: 定义传输数据的协议端口号,以及流程控制和差错校验

协议有: TCP UDP

3) 网络层: 进行逻辑地址寻址,实现不同网络之间的路径选择

协议有: ICMP IGMP IP (IPv4 IPv6) ARP RARP

4) 数据链路层: 建立逻辑连接,进行硬件地址寻址,差错校验等功能

5) 物理层: 建立、维护、断开物理连接。(由底层网络定义协议 )

ps:HTTP叫什么?HTTP/TCP在那一层?

TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时要拆除连接。

由于TCP是面向连接的所以只能用于端到端的通讯。TCP提供的是一种可靠的数据流服务,

采用"带重传的肯定确认"技术来实现传输的可靠性。

TCP还采用一种称为"滑动窗口"的方式进行流量控制,所谓窗口实际表示接收能力,用以限

制发送方的发送速度。

3 计算机网络五层模型-网络层次划分

4 TCP连接-三次握手

字段 全称 名称 解释

seq sequance 序列号

TCP连接中传送的字节流中的每一个字节都

按顺序编号。也叫报文段序号。

ack acknowledge 确认号

用于接收方发送给发送方的确认报文,表明

到(ack-1)为止所有的数据都已正确收到

SYN synchronize 请求同步标志

请求建立连接,设置序列号为 1

SYN=1 且 ACK=0时,表明这是一个连接请

求报文段。

对方同意建立连接后,应在响应报文段中让

SYN=1 ACK=1

ACK acknowledge 确认标志

确认是否有效,一般置为 1

TCP规定在建立连接后,所有传送的报文段

都必须让ACK=1

FIN Finally 结束标志

用于释放一个连接

FIN=1表示此报文段的发送方数据已发送完

毕,并要求释放运输层连接

TCP连接建立过程:

1) 第一次握手(请求建立连接):

客户端发送连接请求报文。Client将标志位SYN置为 1 ,随机为序列号产生一个值seq=x,并

将该数据报发送给Server,Client进入SYN_SENT状态,等待Server确认。

2) 第二次握手(应答):

服务端接收连接请求后回复ACK确认报文,并为这次连接分配资源。Server收到数据包后由

标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为 1 ,ack=x+1,随

机产生一个值seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD

状态。

3) 第三次握手(建立连接):

客户端接收到ACK报文后也向服务端发送ACK报文,并分配资源,此包发送完毕,客户端和

服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。Client收到确认后,检查

ack是否为x+1,ACK是否为 1 ,如果正确则将标志位ACK置为 1 ,ack=y+1,并将该数据包发

送给Server。Server检查ack是否为y+1,ACK是否为 1 ,如果正确则连接建立成功,Client和

Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据

了。

1) Client端发起中断连接请求,也就是发送FIN报文;

2) Serer端接收到FIN报文后,先发送ACK报文,确认接收到了Client端的请求,然后进入准

备状态,此时Server端如果还有没发送完的数据,可以继续发送;

3) Client端接收到Server端的ACK报文,进入FIN_WAIT状态,继续等待Server端的FIN报

文;

4) 当Server端确定数据已发送完成,则向Client端发送FIN报文;

5) Client端接收到FIN报文后,再次发送ACK报文,然后进入TIME_WAIT状态,如果Server

端没有接收到ACK报文可以重传;

6) Server端收到ACK后,断开连接,Client端等待2MSL后依然没有收到回复,则证明Server

端已正常关闭,Client端也可以关闭连接了。TCP连接就这样关闭了。

问题:为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状

态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假

想网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢

失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到

5 TCP断开连接-四次挥手

ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该

ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待

2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所

谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存

活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再

次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

socket的基本组成:ip地址和端口号

就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。是TCP协议的一个运

用实例。

客户端代码示例:

6 TCP与UDP的区别

TCP

UDP(主要用于嵌入式设

备)

连接

是面向连接的运输层协议,应用程序在使用

TCP之前,必须先建立TCP连接,在传送数

据完毕之后,必须释放连接

是无连接的

交付

提供可靠的报文交付,因为TCP是全双工通

信,TCP连接的两端都有发送缓存和接受缓

尽最大努力交付

首部大小 20 字节 8 字节

拥塞控制

有,在接收方来不及处理收到的数据时,及

时告诉发送方降低发送速率

19. (Socket,反射)

1. Socket

1 package server;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.PrintWriter;
服务端代码示例:
7 import java.net.Socket;
8 import java.net.UnknownHostException;
9
10 public class Client {
11
public static void main(String[] args) throws UnknownHostException, IOException
{
12
13 //项目需求:
14 //将用户键盘输入的内容,发送到服务端。
15 int serverPort = 8081;
16 String serverIP = "127.0.0.1";
17 //客户端服务端都要有socket类,该类提供传输的路径。
18 Socket socket = new Socket(serverIP,serverPort);
19 //---获取系统的字节流,并转换成字符流。转成字符流之后再读取。
20 InputStreamReader reader = new InputStreamReader(System.in);
21 BufferedReader bfReader = new BufferedReader(reader);
22 //使用socket专门的Writer,后面PrintWriter要(指定输出流)
//我新建了一个输出流工具,肯定需要指定往哪个输出流里写,
socket.getOutputStream()就是取
23
24 //客户端到服务器的输出流。
PrintWriter socketOut = new PrintWriter(socket.getOutputStream());
25
26 //通过字符输入流读取工具,读取一行用户输入的信息。
27 String string = bfReader.readLine();
28 //输出流工具.println方法(需要写入输出流的文本)
29 socketOut.println(string);
30 //刷新缓存,将数据写入输出流。
31 socketOut.flush();
32 //刷新缓存,将数据写入输出流。
33 }
34 }
1 package server;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.net.ServerSocket;
println()和write()的区别:
1 )write方法只会写入字符,不会写入换行符和回车符。
2 )println方法是写入一行数据,会自带换行符。
3 )当readLine()方法只有读取到\n或者\r的时候,代表数据读取完毕。
4 )否则会一直继续读取输入流的数据,直到有换行符出现。
Java的反射机制是指在程序的运行状态中,可以创建任意一个类的对象,可以了解任意一个类的成员变
量和方法,可以调用任意一个对象的变量和方法。
这种动态获取程序信息以及动态调用对象的功能称为java语言的反射机制。 反射被视为动态语言的关
键。
Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class
字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。 所以要想完
成反射操作,就必须首先认识Class类。
需要注意的是:
1)通过Class类的对象,可以知道他所描述的类的信息;
2) Class 不是所有类的父类(是 Object) ;
7 import java.net.Socket;
8 public class Server {
9
10 public static void main(String[] args) throws IOException {
11 int port = 8081;
12 ServerSocket serverSocket = new ServerSocket(port);
13 Socket socket = serverSocket.accept();
InputStreamReader reader = new
InputStreamReader(socket.getInputStream());
14
15 BufferedReader bfReader = new BufferedReader(reader);
System.out.println("已接收到信息:" + bfReader.readLine());
16
17 }
18 }

2. 反射

2.1 定义:

2.2 Class及其所属方法

1. Class (java.lang.Class) 是什么?

2. 声明一个class:

1 //声明一个Class类,调用一个类的class文件,Class相当于是.class文件的管理类
2 Class<Demo1> d = Demo1.class;
3 Demo1 demo1 = (Demo1) d.newInstance();

3. .getField("变量名"),不包括私有变量,.getDeclarField("变量名"),包括私有变量,返回

Field类型

1 Field f1 = d.getDeclaredField("name");

4. .getDeclarFields()获取所有成员变量的数组,返回一个Field的数组;

1 Field[] f1 = d.getDeclaredFields();
2 for (Field field : f1) {
3 System.out.println(field.getName());
4 }

5. .getDeclaredMethod(String name,Class... parameterType)根据方法名和指定的参数列

表数据类型,获取方法对象。

1 Method method = d.getDeclaredMethod("speak", String.class,int.class);
2 method.invoke(demo2,"abc",17);

6. .getConstructor()获取一个类的构造方法对象

Constructor<Demo1> constructor = d.getConstructor(String.class); ///此处括号,构造参数为
空,则不写,有参数就写类型.class
1
2
3 System.out.println(constructor);
1 )通过new关键字创建对象
2 )通过反射的Class类的newInstance()方法创建对象
1 )field类是记录对象属性的类。
2 )可以操作类中私有,以及公有等全部属性和属性的信息。
1 .getName()获取属性名称
2 .getType()获取属性类型
3 .get(obj),获取obj对象中当前属性的值。
4 .set(obj,obj value) ,给obj对象中的当前属性赋值。
5 .setAccessible(true),启动/禁止访问控制权限,即开启和关闭私有属性的访问权限。
注意:私有属性不能用get直接访问到属性值,必须在调用get之前开启访问权限。

2.3 创建Class类对象的三种方法

1 //1 类名.class
2 Class<Demo1> c1 = Demo1.class;
3
4 //2 通过类对象调用getClass方法
5 Demo1 d1= new Demo1();
6 Class c2 = man.getClass();
7
8 //3 根据完整包路径,获取Class对象
9 Class c3 = Class.forName("demo2.Demo1");

2.4 创建一个类对象有几种方式?

3 Field类

3.1 定义:

3.2 常用方法:

4 反射概念综合思维导图

对于反射的解释:

1 :什么叫反射?
如上图所示。
2 :创建一个Class有几种方式?
3 :创建一个类有几种方式?
1 )通过new关键字创建对象
2 )通过反射的Class类的newInstance()方法创建对象
3 )反序列化
正则表达式用于处理字符串,它通过约束字符的排列规则,可以:
1 验证字符串是否匹配正则规则;
2 检索符合规则的字符串;
完整的正则表达式包括:
1. 特殊字符(或“元字符”)
2. 文字(或“普通文本字符”)

Class类方法的综合思维导图

5 相关面试问题:

1 //1 类名.class
2 Class<Demo1> c1 = Demo1.class;
3
4 //2 通过类对象调用getClass方法
5 Demo1 d1= new Demo1();
6 Class c2 = man.getClass();
7
8 //3 根据完整包路径,获取Class对象
9 Class c3 = Class.forName("demo2.Demo1");

20 (正则表达式)

1 简介

2 元字符

2.1元字符列表

形如(子表达式a|子表达式b)这样的结构称为“多选结构”,|两边的子表达式称为“多选分支”。
注意: * 和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个? 就可以
实现非贪婪或最小匹配。
贪婪匹配 :贪婪模式会尽可能多的匹配
非贪婪匹配: 非贪婪模式会尽可能少的匹配
当? 紧跟在任何一个其他限制符*、+、?、{n}、{n,}、{n,m}后面时,匹配模式都是非贪婪的。
字符 关键词 解释
\d 数字字符 匹配一个数字字符,等价于[0,9]

\D (^) 匹配一个非数字字符

\n 换行符 匹配一个换行符
\r 回车符 匹配一个回车符
\s 空白字符 匹配任何空格字符,包括空格、制表符、换页符等,等价于[\f\n\r\t\v]

\S (^) 匹配任何非空白字符

\w 单词字符 匹配一个单词字符(包括下划线字符),等价于[A-Za-z0-9]

\W (^) 匹配任何非单词字符

. 匹配任意一个字符
\ (反斜杠)转义字符,将特殊字符转义成普通字符

2.2 子表达式( )

() 标记一个子表达式,一个子表达式可以 匹配一段字符串
| 意为'或',|两边的子表达式称为多选分支

2.3 限制符

? 匹配前面的子表达式或字符 0 或 1 次 至多 1 次
* 匹配至少 0 次 [0,n]
+ 匹配至少 1 次 [1,n]
{min,max} 匹配至少min次,至多max次 [min-max]
{n} 匹配n次
{n,} 至少匹配n次
字符组用 [] 表示,一个字符组匹配 一个字符的位置 ,且该位置可以匹配字符组中的任一个字符。
字符组元字符 - (连字符),表示一个范围,注意如果用在字符组开头,如[-]或[^-],则只代表是一
个 - 字符,不表示范围、
排除型字符组:[^...] 匹配 除字符组内所有字符 之外的 任何字符
注意:在字符组内部,元字符的定义规则(及他们的意义)是不一样的。
例如,在字符组外部,点号是元字符,但是在内部则不是如此。相反,连字符只有在字符组内部才是元
字符,否则就不是。
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。
要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。
该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。
与Pattern 类一样,Matcher 也没有公共构造方法。
你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

2.5 脱字符

^ 匹配文本的开始位置,^称为脱字符
$ 匹配文本的结束位置

3 字符组[]

3.1 连字符

3.2 排除字符

4 相关类

5 代码示例

1 //1 正则校验
2 //写一个正则表达式,匹配手机号
3
4 String str = "31415";
增加引用,引用计数加一,减少引用,引用计数减一,当引用降为 0 时,被垃圾回收
存在的问题:循环引用:AB对象互相引用,引用数都为 1 ,引用计数永远不会归零,
扫描堆中的每一个对象,查看对象有没有被根对象直接或间接的引用,如果没有那么就可以被回收。
强引用:日常使用的new关键字新建的对象都是强引用。只要能从根节点找到该对象,就不会被回收。
5 String regx = "\\d\\d+";
6 boolean tar = Pattern.matches(regx, str);
7 System.out.println(tar);
8
9 //2 正则检索
10 String ip = "192.168.1.141";
11 //
12 String r2 = "\\d{2,}?";
13 //编译指定的正则表达式
14 Pattern p2 = Pattern.compile(r2);
15 //将传入的字符串匹配指定的正则表达式
16 Matcher m = p2.matcher(ip);
17 while(m.find()) {//向下查找一次,是否存在匹配的字符串,返回布尔值
18 System.out.println(m.group());//打印匹配到的字符串
19 }

21 GC垃圾回收

1.如何判断一个对象可以被回收?

1.1引用计数法

1.2可达性分析算法

1.2.1根对象:是可达性分析算法的起点

1.2.2那些对象可以作为根对象?

虚拟机栈(栈帧中的本地变量表)中引用的对象
本地方法栈中JNI(即一般说的Native方法)引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象

1.3四种引用

软引用:当没有强引用,且内存不足时,会被回收。
弱引用:当没有强引用,只要发生垃圾回收,就会被回收。
虚引用:是最弱的引用关系,建立虚引用只是为了能在对象被回收时得到一个系统通知。
把没有引用的对象标记出来,然后清除。
其实并没有清除空闲内存的数据,只是记录了需要清除位置的开始和结束内存地址,需要分配新的内存
时会覆盖记录的内存。
缺点 :空间不连续,造成内存碎片。
在标记清除的基础上,将不需要回收的对象移动到一起,避免了空间碎片的产生。
缺点:步骤较多,效率较低。
先标记,从from区将不需要回收的对象复制到to区,然后再将to区交换为from区。
优点:不会产生碎片,但是占用空间相对较大。

在实际运用时,三种垃圾回收算法都会根据情况使用。

相关概念:
堆内存分区: 新生代+老年代
新生代: 新生代分为 伊甸园,幸存区from,幸存区to。 触发的回收叫 minor GC
老年代: 当新生代中的对象年龄达到阈值时晋升到老年代。触发的回收叫 major GC
名词解释 :
STW: stop the world 不论发生哪种垃圾回收都会触发STW,只是时间长短不同。
暂停其他用户的线程,等垃圾回收结束后,用户线程再恢复运行。
Full GC :当老年代内存不足,且尝试minor GC后内存仍然不足的时候,会发生Full GC,会回收整个堆
的内存,包括新生代和老年代。
三色标记法:
黑色:已标记,灰色:标记中,白色:未标记。
白色的未标记的是垃圾。

2.垃圾回收算法

2.1标记清除

2.2标记整理

2.3复制

3.垃圾分代回收

评论

发送评论 编辑评论


				
上一篇
下一篇