Java期末总复习
1、JDK:Java的开发环境JRE:Java的运行环境JVM:运行Java程序的核心虚拟机2、面向对象编程(oop)的特点:①封装性②继承性③多态性3、Java运行机制:源文件(.java)编译字节码文件(.class)解析机器码文件4、JDK–>开发工具、JRE(Java基础类库,JVM)5、Java特点:①简单易用②安全可靠③跨平台④面向对象⑤支持多线程6、JDK是整个Java
1、 JDK:Java的开发环境 JRE:Java的运行环境 JVM:运行Java程序的核心虚拟机
2、 面向对象编程(oop)的特点:①封装性②继承性③多态性
3、 Java运行机制:源文件(.java)编译字节码文件(.class)解析机器码文件
4、 JDK–>开发工具、JRE(Java基础类库,JVM)
5、 Java特点:①简单易用②安全可靠③跨平台④面向对象⑤支持多线程
6、 JDK是整个Java的核心,其中包括Java编译器、Java运行工具、Java文档生成工具、Java打包工具等。
7、 JRE是Java运行环境,是提供给普通用户使用的,JRE工具中只包含Java运行工具,不包含Java编译工具。
8、 不同的操作系统需要不同版本的Java虚拟机好处:有效地解决了程序设计语言在不同操作系统编译时产生不同机器代码的问题,大大降低了程序开发和维护的成本。
9、 Java中的标识符:标识符可以由任意顺序的大小写字母,数字,下划线(_)和美元符号($)组成,但标识符不能以数字开头,也不能是Java中的关键字。
10、 变量类型决定了变量的数据性质、范围、存储在内存中所占的字节数以及可以进行的合法操作。
11、 在Java中,一个小数会被默认为double类型的值,因此在为一个float类型的变量赋值时,所赋值的后面一定要加上字母F(或者小写f),而为double类型的变量赋值时,可以在所赋值的后面加上字符D(或小写d),也可以不加。
12、 在程序中也可以为一个浮点数类型变量赋予一个整数数值。
13、 自动类型转换(隐式类型转换):当把一个类型取值范围小的数值直接赋给另一个取值范围大的数据类型变量时,系统就会进行自动类型转换,否则需要进行强制类型转换
14、 强制类型转换(显式类型转换):指的是两种数据类型之间的转换需要显示地声明。当两种类型彼此不兼容,或者目标类型取值范围小于源类型时,自动类型转换无法进行,这时需要进行强制类型转换。将取值范围大的数据类型的变量值赋值给取值范围小的数据类型的变量时,就可能造成数据的丢失,所以系统默认不支持这种行为,只能由开发者自己决定是否进行强制类型转换。
15、 Java中定义常量只需要在定义变量的语法基础上加上一个final关键字修饰即可。
16、 在进行除法运算时,当除数和被除数都为整数时,得到的结果也是一个整数,如果除法运算有小数参与,得到的结果会是一个小数。
17、 在进行取模(%)运算时,运算结果的正负取决于被模数(%左边的数)的符号,与模数(%右边的数)的符号无关。例如,(-5)%3的结果为-2,而5%(-3)的结果为2。
18、 在使用+=,-=,*=,/=,%=运算符进行赋值时,强制类型转换会自动完成,程序不需要做任何显式的声明。
19、 “==”对于引用类型,比较的是地址,而不是具体值。
20、 &:与。&&:短路与。|:或。||:短路或。
21、 >>:右移。>>>:无符号右移。
22、 在嵌套循环结构中,如果想要在内层循环中使用break语句跳出外层循环,则需要预先对外层循环进行标记,然后使用break语句跳出指定的外层循环。
23、 数组的定义的三种方式:
① 数据类型 []数组名 = new 数组类型[数组长度];
② 数组类型 []数组名 = new 数组类型[]{数组元素0,数组元素1…};
③ 数组类型 []数组名 = {数组元素0,数组元素1,…};
24、 Java中不同数据类型元素的初始值表
25、 在Java中,为了方便获得数组的长度,提供了一个length属性,在程序中可以通过“数组名.length”的方式来获得数组的长度,即数组元素的个数。
26、 多维数组定义方式:
① int[][] arr = new int[3][4];
② int[][] arr = new int[3][];
③ int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
27、 面向对象的特征:封装、继承、多态。
28、 将未赋值(没有被初始化)的变量称为声明变量,而赋值(初始化)的变量称为定义变量。
29、 在创建Person对象时,程序会占用两块内存区域,分别是栈内存和堆内存。其中Person类型的变量P被存放在栈内存中,它是一个引用,会指向真正的对象;通过new Person()创建的对象则放在堆内存中,这才是真正的对象。
30、 Java将内存分为两种,即栈内存和堆内存。其中栈内存用于存放基本类型的变量和对象的引用变量(如Person P),堆内存用于存放由new创建对象和数组。
31、 当没有任何变量引用这个对象时,它将称为垃圾对象,不能再被使用。
32、 在Java中,针对类,成员方法和属性提供了4种访问级别,分别是private,default,protected和public。4种级别由小到大依次列出:privatedefaultprotectedpublic。
33、 Java中的4种访问控制级别:
① private(当前类访问级别):如果类的成员被private访问控制符来修饰,则这个成员只能被该类的其他成员访问,其他类无法直接访问。类的良好封装就是通过private关键字来实现的。
② default(包访问级别):如果一个类或者类的成员不使用任何访问控制符修饰,则称它为默认控制级别,这个类或者类的成员只能被本包中的其他类访问。
③ protected(子类访问级别):如果一个类的成员被protected访问控制符修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问。
④ public(公共访问级别):这是一个最宽松的访问控制级别,如果一个类或者类的成员被public访问控制符修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是否在同一个包中。
34、 如果一个Java源文件中定义的所有类都没有使用public修饰,那么这个Java源文件的文件名可以是一切合法的文件名,如果一个源文件中定义了一个public修饰的类,那么这个源文件的文件名必须与public修饰的类的类名相同。
35、 封装的具体实现过程:在定义一个类时,将类中的属性私有化,即使用private关键字来修饰,私有属性只能在它所在类中被访问,如果外界想要访问私有属性,需要提供一些使用public修饰的公有方法。
36、 Java允许在一个程序中定义多个名称相同,但是参数的类型或个数不同的方法,这就是方法的重载。
37、 方法重载与返回值类型无关,它只需要满足两个条件:一是方法名相同;二是参数个数或参数类型不同。
38、 如果需要在实例化对象的同时就为这个对象的属性进行赋值,可以通过构造方法来实现。
39、 构造方法需同时满足以下三个条件:
① 方法名与类名相同;
② 在方法名的前面没有返回值类型的声明;
③ 在方法中不能使用return语句返回一个值,但是可以单独写return语句来作为方法的结束。
40、 与普通方法一样,构造方法也可以重载,在一个类中可以定义多个构造方法,只要每个构造方法的参数类型或参数个数不同即可,在创建对象时,可以通过调用不同的构造方法来为不同的属性进行赋值。
41、 在Java中每个类都至少有一个构造方法,如果在一个类中没有显式地定义构造方法,系统会自动为这个类创建一个默认的构造方法,这个默认的构造方法没有参数,在其方法体中没有任何代码,即什么也不做。一旦为该类定义了构造方法,系统将不再提供默认的无参构造方法。在一个类中,如果定义了有参的构造方法,最好再定义一个无参的构造方法。
42、 this来指代当前对象,用于在方法中访问对象的其他成员,this的常见三种用法
① 通过this关键字调用成员变量,解决与局部变量名称冲突问题。
② 通过this关键字调用成员方法。
③ 通过this关键字调用构造方法。构造方法是在实例化对象时被Java虚拟机自动调用的,在程序中不能像调用其他方法一样去调用构造方法,但可以在一个构造方法中使用“this([参数1,参数2,…])”的形式来调用其他的构造方法。
43、 在使用this调用类的构造方法时,应注意以下几点:
① 只能在构造方法中使用this调用其他的构造方法,不能在成员方法中使用。
② 在构造方法中,使用this调用构造方法的语句必须是该方法的第一条执行语句,且只能出现一次。
③ 不能在一个类的两个构造方法中使用this相互调用。
44、 静态变量可以被所有实例所共享。
45、 static关键字只能用于修饰成员变量,不能用于修饰局部变量,否则编译会报错。
46、 静态方法不需要创键对象就可以直接通过类名调用。
47、 静态方法,只能调用静态成员方法和使用静态数据成员。
48、 在Java类中,使用一对大括号包围起来的若干行代码被称为一个代码块,用static关键字修饰的代码块被称为静态代码块。在类被加载时,静态代码块会执行,由于类只加载一次,因此静态代码块也只执行一次,在程序中,通常会使用静态代码块来对类的成员变量进行初始化。静态代码块在类第一次使用时才会被加载,并且只会加载一次。
49、 静态代码块:定义在主类中,会优先于main()方法执行。静态代码块定义在非主类中,会优于构造方法执行。
50、 单继承,怎样实现继承?子类名与父类名都是必选的,并且子类与父类之间要使用extends关键字实现继承关系。
51、 子类在继承父类时,会自动拥有父类所有公共的成员。在类的继承中,需要注意一些问题:
① 在Java中,类只支持单继承,不允许多重继承。
② 多个类可以继承同一个父类。
③ 在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类。
52、 在继承关系中,子类会自动继承父类中公共的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。需要注意的是,子类中重写的方法需要和父类被重写的方法具有相同的方法名,参数列表以及返回值类型。
53、 子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限。
54、 在Java中专门提供了一个super关键字来访问父类的成员:
① 使用super关键字调用父类的成员变量和成员方法:super.成员变量 super.成员方法([参数1,参数2…]);
② 使用super关键字调用父类的构造方法:super([参数1,参数2…])。
55、 通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次,否则程序在编译期间就会报错。
56、 在子类的构造方法中一定会调用父类的某个构造方法。这时可以在子类的构造方法中通过super关键字指定调用父类的哪个构造方法,如果没有指定,在实例化子类对象时,会默认调用父类无参的构造方法。如果没有特殊需求,当定义了有参构造方法后,尽量在类中再显式地定义一个无参构造方法,这样可以避免该类被继承时出现错误。
57、 Object类中自定义了一些方法:
58、 final关键字可用于修饰类、变量、方法将具有以下特性:
① final修饰的类不能被继承。
② final修饰的方法不能被子类重写。
③ final修饰的变量是常量,只能赋值一次。
59、 当一个类的方法被final关键字修饰后,这个类的子类将不能重写该方法。
60、 当局部变量使用final关键字进行修饰时,可以在声明变量的同时对变量进行赋值,也可以先声明变量然后再进行有且只有一次的赋值。而当成员变量被final修饰时,再声明变量的同时必须进行初始化赋值,否则程序编译报错。
61、 抽象方法必须使用abstract关键字来修饰,并且在定义方法时不需要实现方法体。当一个类中包含了抽象方法,那么该类也必须使用abstract关键字来修饰。
62、 包含抽象方法的类必须定义给抽象类,但抽象类中可以不包含任何抽象方法。另外,抽象类是不可以被实例化的。子类须重写父类abstract方法,否则仍为abstract类。
63、 接口是一种特殊的抽象类,它不能包含普通方法,其内部所有方法都是抽象类,它将抽象进行得更加彻底,默认全部成员变量为:public static final,默认全部成员方法为:public abstract。在JDK8中,对接口进行了重新定义,接口中除了抽象方法外,还可以有默认方法和静态方法(也叫类方法),默认方法使用default修饰,静态方法使用static修饰,并且这两种方法都允许有方法体。
64、 一个类可先extends一个类,再implements多个接口,extends关键字必须位于implements关键字之前。一个接口可以extends多个接口,接口不能implements接口。
65、 接口中的静态方法可以直接使用接口名调用,需要注意的是,接口的实现类,必须实现接口中的所有抽象方法,否则,程序编译报错。
66、 多态条件:继承、重写、父类引用指向子类对象(向上转型)。程序在编译时自动识别具体的子类对象,从而选择性地调用对应的方法,这就是Java中多态性的体现。
67、 向上转型不能用父类引用使用子类中非继承的成员(将子类对象当作父类使用时不需要任何显式声明,需要注意的时,此时不能通过父类变量去调用子类特有的方法)。
68、 在进行对象向下类型转换时,必须转换为本质类型,否则转换时会出现错误。
69、 instanceof:它可以判断一个对象是否为某个类(或接口)的实例或者子类实例。:对象 instanceof 类。
70、 在一个类中除了可以定义成员变量、成员方法,还可以定义类,这样的类被称作成员内部类。在成员内部类中,可以访问外部类的所有成员,包括成员变量和成员方法。成员内部类可以访问外部类所有成员,同时外部类也可以访问成员内部类的所有成员。
① 外部类使用内部类成员,先定义内部类对象。
② 内部类直接访问外部类成员。
③ 其他类访问内部类成员、先创建外部类对象,再用外部类对象创建内部类对象。
71、 在Java中调用某个方法时,如果该方法的参数是一个接口类型,除了可以传入一个参数接口实现类,还可以使用匿名内部类实现接口来作为该方法的参数。匿名内部类其实就是没有名称的内部类。
72、 Throwable有两个直接子类Error和Exception,其中Error代表程序中产生的错误,Exception代表程序中产生的异常。
73、 处理编译时异常有两种方式,具体如下:
① 使用try…catch语句对异常进行捕获处理。
1) try中:发生异常,其余的语句不会执行。
2) catch:可0~多个,从上往下类型匹配,取其一。
3) finally:可0~1个,除非System.exit(),否则一定会执行。该代码块并不受return语句和程序异常的影响。
② 使用throws关键字声明抛出异常,让调用者对其处理。
74、 在Java中,将异常抛出需要使用throws关键字来实现,该关键字需要写在方法声明的后面,throws后面需要声明方法中发生异常的类型,通常将这种做法称为方法声明抛出一个异常。
75、 throw用于方法体内,并且抛出的是一个异常类对象,而throws关键字用在方法声明中,用来指明方法可能抛出的多个异常。通过throw关键字抛出异常后,还需要使用throws关键字或try…catch对异常进行处理。需要注意的是,如果throw抛出的是Error、RuntimeException或它们的子类异常对象,则无须使用throws关键字或try…catch…对异常进行处理。
76、 在Java中,当一个对象成为垃圾后仍会占用内存空间,时间一长,就会导致内存空间的不足,Java虚拟机会自动回收垃圾对象所占用的内存空间。如果程序中某个对象不再有任何引用对象引用它,它就进入了可恢复状态。当对象失去了所有引用变量的关联,且系统已经调用所有对象的finalize()方法后依然没有使该对象变成可用状态,那么这个对象将永久地失去引用,变成不可用状态,只有当一个对象处于不可用状态时,系统才会真正地回收该对象所占用的内存空间。
77、 除了等待Java虚拟机进行自动垃圾回收外,还可以通过如下两种方式强制系统进行垃圾回收:
① 调用System类的gc()静态方法:System.gc()。可System.gc()通知,但不能指定马上回收。
② 调用Runtime对象的gc()实例方法:Runtime.getRuntime().gc()。
78、 Java中的常用类:
79、 Java中的集合就像一个容器,专门用来存储Java对象(实际上是对象的引用)。
80、 ArrayList是List接口的一个实现类,在ArrayList内部封装了一个长度可变的数组对象,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素。
① 创建ArrayList集合:ArrayList list = new ArrayList();
② 向集合中添加元素:list.add(“stu1”);
③ 获取集合中元素个数:list.size();
④ 集合和数组是一样的:索引的取值是从0开始的,最后一个索引是size-1;
81、 Collection集合遍历:
① Iterator遍历集合:当遍历元素时,首先通过调用ArrayList集合的iterator()方法获得迭代器对象,然后使用hasNext()方法判断集合中是否存在下一个元素,如果存在则调用next()方法将元素取出,否则说明已达到了集合末尾,停止遍历元素。
Iterator iterator = list.iterator();
//判断集合中是否存在下一个元素
while(iterator.hasNext()){
Object obj = iterator.next();//取出ArrayList集合中的元素
System.out.println(obj);
}
在调用Iterator的next()方法之前,迭代器的索引位于第一个
② foreach遍历集合
foreach具体语法格式:
for(容器中元素类型 临时变量:容器变量){
//执行语句
}
注:当使用foreach循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改。
82、 Set接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复。
83、 当向HashSet集合中添加一个元素时,首先会调用该元素的hashCode()方法来确定元素的存储位置,然后再调用元素对象的equals()方法来确保该位置没有重复元素。创建HashSet对象:HashSet set = new HashSet();
向Set集合中添加元素:set.add(“Rose”);
Student类重写equals()方法和hashCode()方法:P206
84、 Map中的映射关系是一对一的,其中键对象Key不允许重复。HashMap集合是Map接口的一个实现类,该集合的键和值允许为空,但键不能重复,且集合中的元素是无序的,水平方向以数组结构为主体并在竖直方向以链表结构进行结合的就是HashMap中的哈希表结构。
创建HashMap对象:Map map = new HashMap();
向Map存储键值对元素:map.put(“1”,”Jack”);
85、 Map集合遍历:
1、 Iterator迭代器遍历Map集合:
Set keyset = map.keySet();//获取键的集合
Iterator it = keyset.iterator();//迭代键的集合
while(it.hasNext()){
Object key = it.next();
Object value = map.get(key);//获取每个键所对应的值
System.out.println(key+”:”+value);
}
②
Set entrySet = map.entrySet();//将原有Map集合中的键值对作为一个整体返回
Iterator It =entrySet.iterator();//获取iterator对象
While(it.HasNext()){
Map.Entry entry = (Map.Entry);//获取集合中键值对映射关系
Object key = entry.getKey();//获取Entry中的键
Object value = entry.getValue();//获取Enry中的值
System.out.println(key+”:”+value);
}
这个集合中存放了Map.Entry类型的元素(Entry是Map接口内部类),每个Map.Enery对象代表Map中的一个键值对。
2、 使用forEach方法遍历Map集合
Map.forEach((key,value)->System.out.println(key+”:”+value));
86、 Arrays工具类提供了大量针对数组操作的静态方法
87、 字节流以字节为单位进行数据的读写,每次读写一个或多个字节数据;字符流以字符为单位进行数据的读写,每次读写一个或者多个字符数据。
88、 Java中的I/O流中有4个类为流的顶级类,分别为InputStream,OutputStream,Reader和Writer。
89、 InputStream和OutputStream这两个类是抽象类,不能被实例化
90、 FileInputStream是操作文件的字节输入流,专门用于读取文件中的数据。由于从文件读取数据是重复的操作,因此需要通过循环语句来实现数据的持续读取。FileOutputStream是OutputStream的子类,它是操作文件的字节输出流,专门用于把数据写入文件。
//创建一个文件字节输入流来读取文件
FileInputStream in = new FileInputStream(“test.txt”);
//定义一个int类型的变量b
Int b = 0;
//通过循环来读取文件,当返回值为-1结束循环
While((b = in.read()) != -1){
System.out.println(b);
}
In.close();//关闭流
//创建文件输出流对象,并指定输出文件名称
FileOutputStream out = new FileOutputStream(“out.txt”);
//定义一个字符串
String str = “hello”;
//将字符串转换为字节数组进行写入操作
Out.write(str.getBytes());
//关闭流
Out.close();
注意:如果是通过FileoutputStream向一个已经存在的文件中写入数据,那么该文件中的数据首先会被清空,再写入新的数据。
91、 文件的拷贝P250
92、 在I/O包中提供了两个带缓冲的字节流,分别是BufferedInputStream和BufferedInputStream,它们的构造方法中分别接收InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能
93、 字符流也有两个抽象的顶级父类,分别是Reader和Writer,其中Reader是字符输入流,用于从某个源设备读取字符。Writer是字符输出流,用于向某个目标设备写入字符。
94、 如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从文件中读取一个或一组字符。FileWriter同FileOutPutStream一样,如果指定的文件不存在,就会先创建文件,再写入数据,如果文件存在,则会先清空文件中的内容,再进行写入。
95、 字符流也提供了带缓冲区的字符缓冲流,分别是BufferedReader和BufferedWriter,其中BufferedReader用于对字符输入流进行操作,BufferedWriter用于对字符输出流进行操作。需要注意的是:在BufferedReader中有一个重要的方法readLine(),该方法用于一次读取一行文本。在循环结束时,一定要调用close()方法,否则极有可能会导致部分存在缓冲区中的数据没有被写入目标文件。
96、 File类:创建文件对象:File file = new File(“example.txt”);
97、 布局管理器:
98、 Swing组件中不仅具有JFrame和JDialog这样的顶级窗口,还提供了一些面板组件,也称之为中间容器,这些面板组件不能单独存在,只能放置在顶级窗口容器中。其中最常见的面板组件有两种:JPanel和JScrollPane:
99、 文本组件用于接收用户输入的信息,其中包括文本框(JTextField)、文本域(JTextArea)等,它们都有一个共同的父类JTextComponent,JTextComponent是一个抽象类。
100、 在Swing组件中还提供了仅展示的标签组件,标签组件也是Swing中很常见的组件。Swing中的标签组件主要用到的是JLabel,JLabel组件可以显示文本、图像,还可以设置标签内容的垂直和水平对齐方式。
101、 在Swing中常见的按钮组件有JButton、CheckBox、JRadioButton等,它们都是抽象类AbstractButton类的直接或间接子类。
102、 JComboBox下拉框组件分为可编辑和不可编辑两种形式,对于不可编辑的下拉框,用户只能选择现有的选项列表。对于可编辑的下拉框,用户既可以选择现有的选项列表,也可以自己输入新的内容。需要注意的是,自己输入的内容只能作为当前项显示,并不会添加到下拉框的选项列表中。
103、 在Swing中,创建下拉式菜单需要使用三个组件:JMenuBar(菜单栏)、JMenu(菜单)和JMenuItem(菜单项)。JMenuBar表示一个水平的菜单栏,它用来管理一组菜单,不参与同用户的交互式操作。JMenuBar有一个无参构造方法,创建菜单栏时,只需要使用new关键字创建JMenuBar对象即可。创建完菜单栏对象后,可以调用它的add(JMenu c )方法为其添加JMenu菜单。
104、 在Java中,可以通过三种方式来实现多线程:第一种是继承Thread类,重写run()方法;第二种是实现Runnable接口,重写run()方法;第三种是实现Callable接口,重写call()方法,并使用Future来获取call()方法的返回结果。
105、 线程的生命周期:
106、 某个线程若想被执行必须要得到CPU的使用权。Java虚拟机会按照特定的机制为程序中的每个线程分配CPU的使用权,这种机制被称作线程的调度。在计算机中,线程调度有两种模型,分别是分时调度模型和抢占式调度模型。
107、 在应用程序中,如果要对线程进行调度,最直接的方式就是设置线程的优先级,优先级越高的线程获得CPU执行的机会越大,而优先级越低的线程获得CPU执行的机会越小。在设计多线程应用程序时,其功能的实现一定不能依赖于线程的优先级,而只能把线程优先级作为一种提高程序效率的手段。
108、 如果想要人为地控制线程执行顺序,使正在执行的线程暂停,将CPU使用权让给其他线程,这时可以使用静态方法sleep(long mills),该方法可以让当前正在执行的线程暂停一段时间,进入休眠等待状态,这样其他的线程就可以得到执行的机会。当其他线程都终止后并不代表当前休眠的线程会立即执行,而是必须当休眠时间结束后,线程才会转换到就绪状态。
109、 线程让步可以通过yield()方法来实现,该方法和sleep(long mills)方法有点类似,都可以让当前正在运行的线程暂停,区别在于yield()方法不会阻塞该进程,它只是将线程转换成就绪状态,让系统的调度器重新分配一次。当某个线程调用yield()方法后,域当前线程优先级相同或者更高的线程可以获得执行的机会。注意:在执行线程让步后并不能保证立即执行其他线程。
110、 当在某个线程中调用其他线程的join()方法时,调用的线程将被阻塞,直到被join()方法加入的线程执行完成后它才会执行。
111、 两个线程在运行时都在等待对方的锁,这样便造成了程序的停滞,这种现象称为死锁。
更多推荐
所有评论(0)