JAVA-学习笔记
P1
dos命令基本操作: 1.cd\ : 回到当前目录根目录下
2.dir : 显示当前文件夹下具体文件信息
3.md XXX : 在当前目录下建立新的文件夹XXX
4.rd XXX : 在当前目录下删除指定的文件夹XXX
5.cd XXX(\YY) : 进入当前文件夹下名为XXX(中名为YY的文件夹下)的文件夹
6.cd.. : 退到当前文件夹的上一级
7.del XX(*.YYY) : 删除XX文件夹中所有(YYY类型)文件
8.exit : 退出dos命令行
P2
Java语言特性 : 跨平台性
P3
JDK(java开发工具,编译,打包)中有JRE(运行环境),JRE中有JVM
P23
左移:<< (整体左移,int型数据在内存中占4个字节空间,一字节空间约8位二进制数,4个字节共32位二进制数)
右移:>> (同上,最高位补什么由原有数据的最高位值而定:为0补0,为1补1)
一个数左移变大, eg:3<<2 --> 3*2^2 , 相当于乘以2的移动的位数次幂
右移变小, eg:6>>2 --> 6/(2^2) , 相当于除以2的移动的位数次幂
无符号右移:>>> 无论最高位为几,移动后最高位都补0
P24
&:与运算:将运算的两数换成二进制进行与运算,0&0,0&1,1&0值都为0,只有1&1时值为1
|: 或运算:将运算的两数换成二进制进行与运算,1 | 0,0 | 1,1 | 1值都为1,只有0 | 0时值为0
^:异或运算,将运算的两数换成二进制进行与运算,两位相同值为0,不相同值为1
一个数异或同一个数两次,结果还是该数
P25
交换:m=3,n=8
方法1:int temp = m;
m = n;
n = temp;
方法2:n = m + n; //如果m和n的值非常大,容易超出int范围
m = n - m;
n = n - m;
方法3:n = n ^ m;
m = n ^ m; //(n^m)^m:一个数异或同一个数两次,结果还是该数
n = n ^ m; //(n^m)^n;
P27
三元运算符:变量 = (条件表达式)?表达式1:表达式2;
好处:可以简化 if else 代码;
弊端:因为是一个运算符,所以运算完必须要有一个结果,if,else可以不必有结果
P28
switch语句只能识别 byte、short、int、char四种类型的数据
对于判断数据较少,且符合以上四种类型的数据建议使用switch,效率较高
P35
无限循环的表现形式:
for( ; ; ) { }
while(true) { }
P36
累加思想
计数器思想
P38
金字塔问题:
不是规律的规律:
尖儿朝上,可以改变条件,让条件随着外循环变化。
尖儿朝下,可以调整初始化值,让初始化随着外循环变化。
P39
给循环起名字:
eg: w:for(; ; ;){
q:for(; ; ;){
System.out.println(...);
break w;
continue w;
}
}
P47
局部变量:定义在方法中,方法参数上,for循环中的变量都是局部变量
局部变量都会在栈内存中开辟空间,用完消除
new 出来的实体或对象都在堆内存中,堆内存中的每一个实体都有一个存储位置
堆内存特点:1.内存地址值
2.初始化数据
3.垃圾清理机制
P49
数组:
int型数组初始值:0;
float型数组初始值:0.0f;
double型数组值初始值:0.0;
boolean型数组初始值:false。
char型数组初始化值:'\u0000'(一个空位,相当于空格)
P51
希尔排序最快:三层循环加上位运算
java提供数组排序方法:Arrays.sort(arr);
P60
二维数组:int[][] arr = new int[x][y];
二维数组可以只定义行数而不定义列数:如 int[][] arr = new int[x][]; 此时二维数组中每个一维数组使用默认初始化:初始化为null,即arr[0或...]=null
P64
面向对象:三个特征:封装、继承、多态
P65
成员变量和局部变量:
作用范围:
成员变量作用于整个类中。
局部变量作用于函数中,或者语句中(for循环)。
在内存中的位置:
成员变量:在堆内存中,因为对象的存在,才在内存中存在。
局部变量:存在于栈内存中。
初始化值区别:
成员变量有默认初始化值,可以直接参与运算
局部变量必须赋初始化值,否则不能运算
P66
匿名对象使用方式:一:当对对象的方法只使用一次时,可以用匿名对象来完成,这样写比较简化
如果对一个对象进行多个成员调用,必须给该对象起名。
二:可以将匿名对象作为实际参数进行传递。
P67
封装
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式
好处:将变化隔离
便于使用
提高重用性
提高安全性
封装原则:把不需要对外提供的内容都隐藏起来
把属性都隐藏,提供公共方法对其访问
P70
构造代码块:{}
作用:给对象进行初始化
对象一建立就运行,而且优先于构造函数执行
和构造函数区别:
构造代码块是给所有对象进行统一初始化
而构造函数是给不同对象进行对应的初始化
构造代码块中定义的是不同对象共性的初始化内容
P74
static(静态)关键字
用于修饰成员(成员变量和成员函数)
被static修饰后的成员具备以下特点:
随着类的加载而加载 (随着类的消失而消失)
说明其生命周期最长
优先于对象存在
被所有对象所共享
可以直接被类名调用
使用注意:
静态方法只能访问静态成员(成员方法和成员变量)
非静态方法既可以访问静态,也可以访问非静态
静态方法中不可以写this,super关键字
this代表对象,而静态成员先于对象存在
主函数是静态的
当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用: 类名.静态成员
方法区、共享区、数据区:存放类中的方法、类中的共享数据
实例变量和类变量的区别:
存放位置:
类变量随着类的加载而存在于方法区中;
实例变量随着对象的建立而存在于堆内存中。
生命周期:
类变量生命周期最长,随着类的消失而消失;
实例变量生命周期随着对象的消失而消失。
静态有利有弊:
利处:对对象的共享数据进行单独空间的存储,节省空间,没必要每个对象中都存储一份;
可以直接被类名调用。
弊端:生命周期过长;
访问出现局限性。(静态虽好,只能访问静态)
P75
主函数:public static void main(String[] args){}
public:代表该函数访问权限最大;
static: 代表主函数随着类的加载就已经存在了;
void: 主函数没有具体的返回值;
main:不是关键字,但是是一个特殊的单词可以被jvm识别;
(String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。是字符串类型的数组。
主函数是固定格式的:jvm识别。
jvm在调用主函数时,传入的是 new String[0];
P79
静态代码块:
格式:
static{
静态代码块中的执行语句。
}
特点:随着类的加载而执行,只执行一次,并优先于主函数。
用于给类进行初始化。
P80
Person P = new Person("zhangsan",20);
这句话都做了:
1.因为new用到了Person.class文件,所以先找到Person.class文件并加载到内存中。(在栈内存中建立p变量)
2.执行该类中的 static 代码块(如果有的话),给Person.class类进行初始化。
3.在堆内存中开辟空间,分配内存地址。
4.在堆内存中建立对象的特有属性,并进行默认初始化。
5.对属性进行显式初始化。
6.对对象进行构造代码块初始化。
7.对对象进行对应的构造函数初始化。
8.将内存地址赋给栈内存中的p变量。
P82
设计模式:解决某一类问题最行之有效的方法。
Java中有23种设计模式。
单例设计模式:解决一个类在内存中只存在一个对象。
想要保证对象的唯一:
1.为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象;
2.还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对象;
3.为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
这三步用代码体现:
1.将构造函数私有化;
2.在类中创建一个本类对象;
3.提供一个方法可以获取到该对象。
比如某软件的配置文件,在对该软件的相应属性进行修改后,修改后的配置会保存在配置文件中,当再次打开后会保持修改后的属性。
该配置文件就只能创建一个对象,且外部程序可以调用该文件。
P83
原则:定义单例时建议使用饿汉式
面试时容易问到懒汉式,结合了多线程可以解决安全问题,还可以提高效率。
P84
继承:
1.提高了代码的复用性;
2.让类与类之间产生关系,有了该关系,才有了多态的特性。
P86
聚集关系:has a(谁里面有谁)
聚合:比如一个班级有许多学生;一个球队由许多人组成,球员是球队的一员;
组合(更紧密):比如手、心脏是人的一部分。
P88
覆盖:
1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败;
2.静态只能覆盖静态。
重载:只看同名函数的参数列表。
重写:子父类方法要一摸一样。
P89
子父类中的构造函数:
在对子类对象进行初始化时,父类的构造函数也会运行;
结论:子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数:
因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
子类的实例化过程:
结论:
子类的所有的构造函数,默认都会访问父类中空参数的构造函数
因为子类的构造函数默认第一行有一条隐式的语句 super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问的父类中的构造函数。
注意:super语句一定定义在子类构造函数的第一行。
子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数。
P90
final:最终
如public static final double PI = 3.14;
1.可以修饰类、函数、变量;
2.被final修饰的类不可以被继承;
为了避免被继承,被子类复写功能导致未知问题(底层应用),可以用final修饰类。
3.被final修饰的方法不可以被复写;
4.被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量又可以修饰局部变量;
当在描述事物时,一些数据的出现值是固定的。为了增强阅读性,都给这些值起名,并以final修饰。
常量书写规范:所有字母都大写,若由多个单词组成,单词间通过_连接。
5.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。
P91
抽象类的特点:
1.抽象方法一定定义在抽象类中;
2.抽象方法和抽象类都必须被abstract关键字修饰;
3.抽象类不可以用new创建对象,因为调用抽象方法没意义;
4.抽象类中的方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象使用;
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
特殊:
抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
P95
接口定义时,格式特点:
1.接口中常见定义:常量,抽象方法;
2.接口中的成员都有固定修饰符:
常量:public static final
方法:public abstract
记住:接口中的成员都是public的。
接口:是不可以创建对象的,因为有抽象方法。
需要被子类实现,子类将接口中的抽象方法全部覆盖后,子类才可以实例化。
否则,子类是一个抽象类。
接口可以被类多实现。
接口可以继承接口,且可以多继承。
P99
多态:事物存在的多种体现形态。
1.多态的体现
父类的引用指向自己的子类对象。
父类的引用也可以接收自己的子类对象。
2.多态的前提
必须是类与类之间有关系,要么继承,要么实现。
通常还有一个前提:存在覆盖。
3.多态的好处
多态的出现大大的提高程序的扩展性。
4.多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
5.多态的应用
6.多态的出现代码中的特点(多态使用的注意事项)
P103
在多态(父类引用指向子类对象)中,成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过;如果没有,编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
P106
Object:是所有对象的直接或者间接父类。
该类中定义的是所有对象都具备的功能。
Object类中:
equals 方法:比较对象是否相同
toString 方法:返回对象的字符串表示形式
返回格式:getClass().getName() + '@' + Integer.toHexString(hashCode())
P108
内部类的访问规则:
1.内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式: 外部类名.this
2.外部类要访问内部类,必须建立内部类对象。
直接访问内部类中的成员:Outer.Inner in = new Outer().new Inner();
内部类可以被私有修饰。
访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有。
可以在外部其他类中,可以直接建立内部类对象。
格式: 外部类名.内部类名 变量名 = new 外部类().new 内部类(); (即外部类对象.内部类对象)
如:Outer.Inner in = new Outer().new Inner();
2.当内部类在成员位置上,就可以被成员修饰符所修饰。
比如:private:将内部类在外部类中进行封装。
static:内部类就具备了static的特性。
当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。
在外部其他类中,可以通过: new 外部类.内部类().function(); 直接访问static内部类的非静态成员function();
通过: 外部类.内部类.function(); 直接访问static内部类的静态成员。
注意:当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。
P110
当描述事物时,事物的内部还有事物,该事物用内部类来描述。
因为内部事物在使用外部事物的内容。
P111
局部内部类不能定义静态成员,因为如果局部内部类有静态成员,该局部内部类必须定义为static的,而局部内部类不能用修饰符修饰,所以局部内部类不能定义静态成员。
内部类定义在局部时:
1.不可以被成员修饰符(如private、static)修饰
2.可以直接访问外部类中的成员,因为还持有外部类中的引用。(外部类名.this)
但是不可以访问它所在的局部中的变量,只能访问被 final 修饰的局部变量。(jdk8之后自动加上)
匿名内部类:
1.匿名内部类其实就是内部类的简写格式。
2.定义匿名内部类的前提:
内部类必须是继承一个类或者实现接口。
3.匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4.其实匿名内部类就是一个匿名子类对象。可以理解为带具体内容的对象。
5.匿名内部类中定义的方法最好不要超过3个。
当使用的方法的参数类型是一个接口类型,且该接口里面的函数只有不超过3个,可以定义一个匿名内部类,将匿名内部类作为参数传进去也可以。
P112
问题:
对于严重的,java通过Error类进行描述;
对于Error一般不编写针对性的代码对其进行处理。
对于非严重的,java通过Exception类进行描述。
对于Exception可以使用针对性的处理方式进行处理。
1.异常的处理:
java 提供了特有的语句进行处理。
try{
需要被检测的代码;
}
catch(异常类 变量){
处理异常的代码;(处理方式)
}
finally{
一定会执行的语句;
}
2.对捕获到的异常对象进行常见方法操作。
String getMessage() :获取异常信息。
String toString() :异常名称:异常信息。
void printStackTrace() :异常名称,异常信息,异常出现的位置。
其实 jvm 默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。
在函数上声明异常。
便于提高安全性,让调用处进行处理,不处理编译失败。
对于多异常的处理:
1.声明异常时,建议声明更为具体的异常,这样处理的可以更具体。
2.对方声明几个异常,就对应有几个catch块。
如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。
P116
自定义异常:
当在函数内部出现了 throw 抛出异常对象,那么就必须要给出对应的处理动作。
要么在内部 try catch 处理;
要么在函数上声明让调用者处理。
一般情况下,函数内出现异常,函数上需要声明。
如何定义异常信息:
因为父类中已经把异常信息的操作都完成了。
所以子类只要在构造时,将异常信息通过 super 语句传递给父类。
那么就可以直接通过getMessage方法获取自定义的异常信息。
自定义异常:
必须是自定义类继承Exception。
继承Exception原因:
异常体系有一个特点:因为异常类和异常对象都被抛出,
它们都具备可抛性。这个可抛性是 Throwable 这个体系中的独有特点。
只有这个体系中的类和对象才可以被 throw 和 throws 操作。
P117
throws 和 throw 的区别:
throws 使用在函数上。
throw 使用在函数内。
throws 后面跟的是异常类,可以跟多个,用逗号隔开。
throw 后跟的是异常对象。
P118
RuntimeException:
Exception中有一个特殊的子类异常 RuntimeException 运行时异常。
如果在函数内抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。
之所以不用在函数声明,是因为不需要让调用者处理。
当该异常发生,希望程序停止,因为在运行时,出现了无法继续运算的情况。
希望程序停止后,对代码进行修正。
自定义异常时,如果该异常的发生,无法再继续进行运算。
就让自定义异常继承 RuntimeException 。
对于异常分两种:
1.编译时被检测的异常。
2.编译时不被检测的异常(运行时异常,RuntimeException 以及其子类)
throw 语句存在时下面不能写语句,因为 throw 语句相当于程序结束标志,与return类似。
P120
finally 代码块:定义一定执行的代码。
通常用于关闭资源。
P121
异常-处理语句其他格式:
第一个格式:
try{
}
catch(){
}
第二个格式:
try{
}
catch(){
}
finally{
}
第三个格式:
try{
}
finally{
}
注意:catch 是用于处理异常,如果没有 catch 就代表异常没有被处理过;如果该异常是检测时异常,那么必须声明。
P122
异常在子父类覆盖中的体现:
1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2.如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
3.如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
如果子类方法发生了异常,就必须要进行 try 处理,绝对不能抛。
P124
异常:
是对问题的描述,将问题进行对象的封装。
---------------------
异常体系:
Thorwable
|--Error
|--Exception
|--RuntimeException
异常体系的特点:异常体系中的所有类以及建立的对象都具备可抛性。
也就是可以被 throw 和 throws 关键字所操作。
只有异常体系具备这个特点。
---------------------
throw 和 throws 的用法:
throw 定义在函数内,用于抛出异常对象;
throws 定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开。
当函数内有 throw 抛出异常对象,并未进行 try 处理。必须要在函数上声明,否则编译失败。
注意:RuntimeException 除外。函数内如果抛出的是 RuntimeException 及其子类异常,函数上可以不用声明。
如果函数声明了异常,调用者需要进行处理。处理方法:可以throws ,可以try 。
注意:RuntimeException 除外。函数上如果声明的是 RuntimeException 及其子类异常,调用者可以不用处理。
----------------------
异常有两种:
编译时被检测异常
该异常在编译时,如果没有处理(没有 throws ,也没有 try ),编译失败。
该异常被标识,代表着可以被处理。
运行时异常(编译时不检测)
在编译时,不需要处理,编译器不检查。
该异常的发生,建议不处理,让程序停止。需要对代码进行修正。
----------------------
异常处理语句:
try{
需要被检测的代码;
}
catch(){
处理异常的代码;
}
finally{
一定会执行的代码;
}
有三种格式:
1.
try{
}
catch(){
}
2.
try{
}
catch(){
}
finally{
}
3.
try{
}
finally{
}
注意:1. finally 中定义的通常是 关闭资源代码 。因为资源必须释放。
2. finally 只有一种情况不会执行:当执行到 System.exit(0); ,finally 不会执行。
若在 finally 代码块之前出现 System.exit(0); 语句,则 finally 块 无法执行
System.exit(0); 代表着系统退出,jvm结束。
----------------------
自定义异常:
定义类继承 Exception 或者 RuntimeException
1.为了让该自定义类具备可抛性;
2.让该类具备操作异常的共性方法。
当要定义自定义异常的信息时,可以使用父类已经定义好的功能。
将异常信息传递给父类的构造函数。
class MyException extends Exception {
MyException(String message){
super(message);
}
}
自定义异常:按照java的面向对象思想,将程序中出现的特有问题进行封装。
---------------------------
异常的好处:
1.将问题进行封装;
2.将正常流程代码和问题处理代码相分离,方便阅读。
异常的处理原则:
1.处理方式有两种:try 或者 throws 。
2.调用到抛出异常的功能时,抛出几个,就处理几个。
一个 try 对应多个 catch
3.多个 catch ,父类的 catch 放到最下面。
4.catch 内,需要定义针对性的处理方式。不要简单的定义 printStackTrace ,或输出语句等;也不要不写。
当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。
try{
throw new AException();
}
catch(AException e){
throw e;
}
如果该异常处理不了,但并不属于该功能出现的异常。
可以将异常转换后,再抛出和该功能相关的异常。
或者异常可以处理,但需要将异常产生的和本功能相关的问题提供出去,
让调用者知道,并处理。也可以将捕获异常处理后,转换新的异常。
try{
throw new AException();
}
catch(AException e){
//对AException处理
throw new BException();
}
比如,汇款的例子。
----------------------
异常的注意事项:
在子父类覆盖时:
1.子类抛出的异常必须是父类的异常的子类或者子集。
2.如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能 try 不能 throws。
P126
包:
编译:javac -d . 类名.java (当前目录下)
javac -d x:\xx\xx (x\xx\xx目录下)
运行:java 包名.类名
类名的全名是:包名.类名
包与包之间进行访问,被访问的包中的类以及类中的成员,需要 public 修饰
不同包中的子类可以访问父类中被 protected 权限修饰的成员。
所以,包与包之间可以使用的权限只有两种:public protected
P128
为了简化类名的书写,使用一个关键字:import
import 导入的是包中的类。
建议,不要写通配符 * ,需要用到包中的哪个类,就导入哪个类。
建议定义包名不要重复,可以使用url来完成定义,url是唯一的。
P130
进程:是一个正在执行中的程序。
每一个进程执行,都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。
Java VM 启动的时候会有一个进程 java.exe
该进程中至少一个线程负责 java 程序的执行。
而且这个线程运行的代码存在于 main 方法中。
该线程称之为 主线程 。
扩展:其实更细节说明 jvm ,jvm 启动不止一个线程,还有负责垃圾回收机制的线程。
P131
创建线程的第一种方式:继承 Thread 类。
步骤:
1.定义类继承 Thread 。
2.复写 Thread 类中的 run 方法。
目的:将自定义代码存储在 run 方法。让线程运行。
3.调用线程的 start 方法。
该方法有两个作用:启动线程,调用 run 方法。
通常运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
在某一时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
所以可以把多线程的运行行为看作在互相抢夺 cpu 的执行权。
这就是多线程的一个特性:随机性。
P132
为什么要覆盖 run 方法:
Thread 类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是 run 方法。
也就是说 Thread 类中的 run 方法,用于存储线程要运行的代码。
P134 -------------> 临时状态 (具备执行资格,但没有执行权)
| <----------- 阻塞 --------------------------|
| | |
线程被创建--start()-->运行---------------1.sleep(time)----------------->冻结 (放弃了执行资格)
1.stop() | <--sleep时间到--
| --2.wait()-->
2.run方法结束| <--notify();--
|
消亡
P135
线程有自己的名称(通过线程对象的 getName() 方法获取):
Thread-编号 该编号从0开始。
Thread 类中静态方法 currentThread() 返回当前正在执行的线程对象的引用。
Thread.currentThread()==this
P137
创建线程的第二种方式:实现 Runnable 接口。
步骤:
1.定义类实现 Runnable 接口。
2.覆盖 Runnable 接口中的 run 方法。
将线程要运行的代码存放在该 run 方法中。
3.通过 Thread 类建立线程对象。
4.将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造函数。
因为,自定义的 run 方法所属的对象是 Runnable 接口的子类对象。
所以要让线程去执行指定对象的 run 方法。就必须明确该 run 方法所属的对象。
5.调用 Thread 类的 start 方法开启线程并调用 Runnable 接口子类的 run 方法。
实现 Runnable 接口方式和继承 Thread 类方式有什么区别:
实现方式好处:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
两种方式区别:
继承 Thread :线程代码存放在 Thread 子类 run 方法中。
实现 Runnable :线程代码存放在接口的子类的 run 方法中。
P138
Java对于多线程的安全问题提供了专业的解决方式。
同步代码块。
synchronized(对象){
需要被同步的代码。(哪些语句在操作共享数据)
}
对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取 cpu 的执行权,也进不去,因为没有获取锁。
经典例子:火车上的卫生间。
同步的前提:
1.必须有两个或者两个以上的线程。
2.必须多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程都需要判断锁,较为消耗资源。
P140
如何判断程序是否有安全问题:
1.明确哪些代码是多线程运行代码。
2.明确共享数据。
3.明确多线程运行代码中哪些语句是操作共享数据的。
P141
同步函数用的锁是:this
函数需要被对象调用,那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。
P142
如果同步函数被静态修饰后,使用的锁不是 this 。
因为静态方法中不可以定义this。
静态进内存时,内存中没有本类对象。但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class(该对象在内存中唯一)
P143
懒汉式特点在于实例的延时加载。如果多线程访问时有可能出现安全问题,可以通过加同步解决。
用同步代码块和同步函数都可以,但是有些低效,此时,用双重判定的方法可以稍提高效率。(不必多次判断)
在单例模式懒汉式中,加同步时使用的锁是该方法所属类的字节码文件对象,因为获取该单例的方法由 static 修饰
P144
死锁:
同步中嵌套同步。
P145
线程间通信:
其实就是多个线程在操作同一个资源,但是操作的动作不同。
P147
等待后的线程放在线程池中。需要唤醒时从线程池中唤醒。
还有一个 notifyAll(); 方法,能唤醒线程池中全部线程。
wait、notify、notifyAll 等方法全部用在同步中,因为要对持有监视器(锁)的线程操作。
所以要在同步中使用,因为只有同步才具有锁。
以上操作线程的方法定义在 Object 类中的原因:
因为这些方法在操作同步中的线程时,都必须要标识它们所操作的线程所持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的 notify 唤醒。
不可以对不同锁中的线程进行唤醒。
而锁可以是任意对象,所以可以被任意对象调用的方法定义在 Object 中。
即:等待和唤醒必须是同一把锁。
P149
对于多个生产者和消费者,为什么要定义 while 判断标记。
原因:让被唤醒的线程再一次判断标记。
定义 notifyAll 的原因:因为需要唤醒对方线程。
因为只用 notify ,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。
P150
JDK1.5中提供了多线程升级解决方案。
将同步 synchronized 替换成 Lock 操作。
将 Object 中的 wait 、notify、notifyAll,替换成了Condition对象。该对象可以通过 Lock 锁进行获取。
P151
stop 方法已经过时。
如何停止线程:run 方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让 run 方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。就不会读取到标记,那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行的状态中来,这样就可以操作标记让线程结束。
Thread 类提供该方法 interrupt();
P152
守护线程:
Thread 类中有 setDaemon(boolean on) 方法,on 如果为 true ,可以将调用该方法的线程定义为守护线程
该方法必须在启动线程前调用。
当正在运行的线程都是守护线程时,Java虚拟机退出。
P153
join:
当A线程执行到了B线程的 .join() 方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
P154
Thread类有一个方法 yield 可以暂停当前线程
setPriority() 方法可以设置线程优先级
P155
String s1 = "abc"; s1是一个类类型的变量,"abc"是一个对象。
String s2 = new String("abc");
s1 和 s2 有什么区别:
s1在内存中有一个对象。
s2在内存中有两个对象。
字符串最大特点:一旦被初始化就不可以被改变。
String 类复写了 Object 类中的 equals 方法,该方法用于判断字符串是否相同。
P156
String 类描述字符串事物。提供了多个方法对字符串进行操作。
常见操作:
1.获取。
1.1 字符串中包含的字符数,即字符串的长度。
int length() :获取长度。
1.2 根据位置获取该位置上某个字符。
char charAt(int index)
1.3 根据字符获取该字符在字符串中的位置。
int indexOf(int ch) :返回的是ch字符在字符串中第一次出现的位置。
int indexOf(int ch, int fromIndex) :从fromIndex指定位置开始,获取ch在字符串中出现的位置。
int indexOf(String str) :返回的是str字符串在字符串中第一次出现的位置。
int indexOf(String str, int fromIndex) :从fromIndex指定位置开始,获取str在字符串中出现的位置。
int lastIndexOf(int ch)
2.判断。
2.1 字符串中是否包含某一个子串。
boolean contains(String str) :参数类型是CharSequence接口类型,而String 类是该接口的实现类,所以参数可以传字符串。
特殊之处:indexOf(str) :可以索引str第一次出现的位置,如果出现-1,表示该str不在字符串中存在。
所以,也可以用于对指定的子串判断是否包含。
而且该方法既可以判断,又可以获取出现的位置。
2.2 字符串中是否有内容。
boolean isEmpty() :原理就是判断长度是否为0
2.3 字符串是否以指定内容开头。
boolean startsWith(String str);
2.4 字符串是否以指定内容结尾。
boolean endsWith(String str);
2.5 判断字符串内容是否相同。 复写了Object类中的equals方法。
boolean equals(String str);
2.6 判断内容是否相同,并忽略大小写。
boolean equalsIgnoreCase(String str);
3.转换。
3.1 将字符数组转换成字符串。
构造函数:String(char[])
String(char[], int offset, int count) :将字符数组中的一部分转成字符串。
静态方法:static String copyValueOf(char[]);
static String copyValueOf(char[] data, int offset, int count);
static String valueOf(char[]);
3.2 将字符串转换成字符数组。**
char[] toCharArray();
3.3 将字节数组转成字符串。
String(byte[])
String(byte[], int offset, int count)
3.4 将字符串转成字节数组。
byte[] getBytes();
3.5 将基本数据类型转换成字符串。
static String valueOf(int)
static String valueOf(double)
如:3+"" 相当于 String.valueOf(3);
特殊:字符串和字节数组在转换过程中,是可以指定编码表的。
4.替换。
String replace(oldchar, newchar);
5.切割。
String[] split(regex);
6.子串。获取字符串中的一部分。
String substring(begin); 从指定位置开始到结尾。如果角标不存在,会出现字符串角标越界异常。
String substring(begin, end); 包含头,不包含尾。 s.substring(0,s.length)可获得整个串。
7.转换,去除空格,比较。
7.1 将字符串转成大写或小写。
String toUpperCase();
String toLowerCase();
7.2 将字符串两端的多个空格去除。
String trim();
7.3 对两个字符串进行自然顺序的比较。
int compaerTo(String str); 等于返回0,小于返回负数,大于返回正数。
P164
StringBuffer 是字符串缓冲区。
是一个容器。
特点:
1.长度是可变化的。
2.可以直接操作多个数据类型。
3.最终会通过 toString 方法变成字符串。
是 final 类型的类,不能继承。
C create U update R read D delete
1.存储。
StringBuffer append(...) :将指定数据作为参数添加到已有数据的结尾处。参数类型不包括byte和short类型,但可以转换成 int 类型添加。
StringBuffer insert(index, 数据) :可以将数据插入到指定index位置。
2.删除。
StringBuffer delete(start, end) :删除缓冲区中的数据,包含start,不包含end。
StringBuffer deleteCharAt(index) :删除指定位置的字符。
3.获取。
char charAt(int index);
int indexOf(String str);
int lastIndexOf(String str);
int length();
String substring(int start, int end); 注意返回类型是String,不是StringBuffer。
4.修改。
StringBuffer replace(int start, int end, String str); 包含start,不包含end
void setCharAt(int index, char ch);
5.反转。
StringBuffer reverse();
6.将缓冲区中指定数据存储到指定字符数组中。
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);
srcBegin :源的开始位
srcEnd :源的结束位
dst :目标数组
dstBegin :目标数组的开始位
该方法的用法是将调用该方法的StringBuffer对象从srcBegin位开始到srcEnd(不包含第srcEnd位)的内容赋给dst中从dstBegin开始的地方。
JDK1.5 版本之后出现了StringBuilder。
StringBuffer是线程同步。StringBuffer 的所有公开方法都是 synchronized 修饰的(适合多线程)
StringBuilder是线程不同步。 (适合单线程)
StringBuffer 适用于用在多线程操作同一个 StringBuffer 的场景,如果是单线程场合 StringBuilder 更适合。
开发建议使用StringBuilder
升级三个因素:
1.提高效率; 2.简化书写; 3.提高安全性。
P167
基本数据类型对象包装类。
基本数据类型 引用类型
byte Byte
short Short
int Integer
long Long
boolean Boolean
float Float
double Double
char Character
基本数据类型对象包装类的最常见作用,
就是用于基本数据类型和字符串类型之间做转换。
基本数据类型转成字符串。
基本数据类型+""
基本数据类型 . toString(基本数据类型值);
如:Integer.toString(34); 将34整数变成"34";
字符串转成基本数据类型。
基本数据类型对象包装类 . parseXXX(String); (静态)返回值为该类对应的基本数据类型
如:int a = Integer.parseInt("123");
基本数据类型对象 . xxxValue();
如:Integer i = new Integer("123");
int num = i.intValue();
十进制转成其他进制。
toBinaryString();
toOctalString();
toHexString();
其他进制转成十进制。
parseXxx(String, radix); radix是对应进制,该方法能将 String 记录为radix进制的数,然后转换成十进制。
如:int x = Integer.parseInt("3c", 16); 得x=60
P168
当数值在byte范围内,对于新特性,如果该数值已经存在,不会再开辟空间。
P170
1.add方法的参数类型是Object。以便于接受任意类型的对象。
2.集合中存储的都是对象的引用(地址)。
如:ArrayList al = new ArrayList();
添加元素:al.add(obj);
打印集合:直接输出集合。System.out.println(al);
删除元素:al.remove(obj);
清空集合:al.clear();
集合长度:al.size();
判断某元素是否存在:al.contains(obj);
判断集合是否为空:al.isEmpty();
取交集:al1.retainAll(al2); al1中只会保留和al2中相同的元素。
P171
获取迭代器,取出集合中元素:al.iterator(); 该方法返回一个Iterator接口类型的子类对象。
迭代器:集合的取出元素的方式。
示例1:Iterator it = al.iterator();
while(it.hasNext()){
sop(it.next());
}
示例2:for(Iterator it=al.iterator(); it.hasNext(); ){
sop(it.next());
}
P172
Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList :底层的数据结构使用的是数组结构。线程不同步。 特点:查询速度很快(有角标),但是增删稍慢。
|--LinkedList :底层使用的是链表数据结构。 特点:增删速度很快,查询速度慢。线程不同步。
|--Vector :底层是数组数据结构。是同步的。被ArrayList替代了。
|--Set:元素是无序(存入和取出的顺序不一定一致)的,元素不可以重复。
|--HashSet :底层数据结构是哈希表。线程是非同步的。
|--TreeSet :底层数据结构是二叉树。
可以对 Set 集合中的元素进行排序。
保证元素唯一性的依据:compareTo 方法 return 0 。
TreeSet 排序的第一种方式:让元素自身具备比较性。
元素需要实现 Comparable 接口,覆盖 compareTo 方法。
这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet 排序的第二种方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。
当两种排序都存在时,以比较器为主。
List:
凡是可以操作角标的方法,都是该体系特有的方法。
增:
add(index, element);
addAll(index, Collection);
删:
remove(index);
改:
set(index, element);
查:
get(index);
subList(from, to); 包含from,不包含to
listIterator();
int indexOf(obj) :获取指定元素的位置。
ListIterator listIterator();
List集合特有的迭代器:ListIterator 是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。
因为会发生 ConcurrentModificationException 异常。
所以,在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断、取出、删除的操作。
如果想要其他的操作如添加、修改(set)等,就需要使用其子接口,ListIterator。
该接口只能通过List集合的listIterator方法获取。
P175
枚举(Enumeration)就是 Vector 特有的取出方式。(返回Enumeration接口类型)
枚举和迭代是一样的。
因为枚举的名称及其方法的名称都过长,所以被迭代器取代了。
P176
LinkedList :特有方法:
头 尾
addFirst(); addLast();
getFirst(); getLast(); get 方法获取元素,但不删除元素。
removeFirst(); removeLast(); remove 方法获取元素,并删除元素。如果集合中没有元素,会出现 NoSuchElementException。
在 JDK1.6 出现了替代方法。
offerFirst(); offerLast();
peekFirst(); peekLast(); 获取元素,但不删除元素。如果集合中没有元素,则返回 null 。
pollFirst(); pollLast(); 获取元素,并删除元素。如果集合中没有元素,则返回 null 。
P178
在迭代时,循环中next调用一次,就要hasNext判断一次。
P179
List集合判断元素是否相同(contains方法),依据的是元素的 equals 方法。
contains、remove 方法底层调用的都是 equals 方法。
P180
HashSet 获取元素只能通过迭代器,因为 Set 接口只继承了 Collection 接口中的方法,并未扩展其他方法。
HashSet如何保证元素唯一性:
通过元素的两个方法,hashCode 和 equals 来完成。
如果元素的 HashCode 值相同,才会判断 equals 是否为true。
如果元素的 HashCode 值不同,不会调用 equals 。
复写 hashCode 和 equals 方法要注意写法,equals 方法的参数是Object类型。
P182
在HashSet中,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的 hashCode 和 equals 方法。
P183
TreeSet 中,字符排序按 ASCII 码表的由小到大的自然顺序排序。
P184
TreeSet 中,排序时,当主要条件相同时,一定要判断一下次要条件。
P185
当自定义类实现 Comparable 接口,并复写了其 compareTo 方法后,
若直接 return 1;则输出顺序与输入顺序一致。
若直接 return -1; 则输出顺序与输入顺序相反。
若直接 return 0; 则只输出第一个输入的元素。
P188
泛型:JDK1.5 版本以后出现的新特性。用于解决安全问题,是一个类型安全机制。
好处:
1.将运行时期出现问题 ClassCastException ,转移到了编译时期。
便于解决问题,让运行时期问题减少,安全。
2.避免了强制转换的麻烦。
P189
泛型格式:通过 <> 来定义要操作的引用数据类型。
在使用java提供的对象时,泛型通常在集合框架中很常见。
只要见到 <> 就要定义泛型。 其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
P190
什么时候定义泛型类:
当类中要操作的引用数据类型,不确定的时候,
早期定义Object来完成扩展;
现在定义泛型来完成扩展。
P191
泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
P192
特殊之处:静态方法不可以访问类上定义的泛型。
如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。
P194
当不确定数据类型时,可以使用,泛型通配符:?
? 通配符。也可以理解为占位符。
泛型的限定:
? extends E :可以接收E类型或者E的子类型。 上限。
? super E :可以接收E类型或者E的父类型。 下限。
P196
Map<K,V> 集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。
1.添加。
put(K key, V value)
putAll(Map<? extends K, ?extends V> m)
2.删除。
clear()
remove(Object key)
3.判断。
containsValue(Object value)
containsKey(Object key)
isEmpty()
4.获取。
get(Object key)
size()
values()
entrySet()
keySet()
注:键-值对中的键是唯一的,并且一个键只能映射一个值。但是,一个值可以对应多个键。
Map
|--Hashtable :底层是哈希表数据结构,不可以存入 null 键 null 值。该集合是线程同步的。效率低(jdk1.0)
|--HashMap :底层是哈希表数据结构,允许使用 null 值和 null 键。该集合是不同步的。效率高(jdk1.2)
|--TreeMap :底层是二叉树数据结构,线程不同步。可以用于给 Map 集合中的键进行排序。
Set 集合底层就是使用了 Map 集合。
P198
put :如果出现添加相同的键时,后添加的值会覆盖原有键对应的值。并 put 方法会返回被覆盖的值。
与Set集合中的add方法返回真假不同,Map 集合的 put 方法会返回该键对应的原来的值。
remove :若键存在,返回键对应的值。
values :获取 map 集合中所有的值
可以通过 get 方法的返回值来判断一个键是否存在。通过返回 null 来判断。
P199
map 集合的两种取出方式:
1. Set<k> keySet :将 map 中所有的键存入到Set集合。因为 Set 具备迭代器,
所以可以以迭代方式取出所有的键,再根据 get 方法获取每一个键对应的值。
Map 集合的取出原理:将map集合转成set集合。再通过迭代器取出。
2. Set<Map.Entry<k,v>> entrySet :将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry
P206
集合框架的工具类:
Collections 其中方法全为静态。
Collections 工具类常用方法:
static <T extends Comparable<? super T>> void sort(List<T> list)
static <T> void sort(List<T> list, Comparator<? super T> c)
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
static <T> void fill(List<? super T> list, T obj)
static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
static void reverse(List<?> list)
static <T> Comparator<T> reverseOrder()
static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
static void swap(List<?> list, int i, int j)
static <T> List<T> synchronizedList(List<T> list)
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
static <T> Set<T> synchronizedSet(Set<T> s)
P211
Arrays :用于操作数组的工具类。
里面都是静态方法。
static <T> List<T> asList :将数组变成List集合。
好处:可以使用集合的思想和方法来操作数组中的元素。
注意:将数组变成集合,不可以使用集合的增删方法。
因为数组的长度是固定的。
可以使用:contains
get
indexOf
subList
如果增删,会发生UnsupportedOperationException
如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素。
如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
P212
集合变数组。
Collection 接口中的 toArray 方法。
Object[] toArray()
<T> T[] toArray(T[] a)
1.指定类型的数组到底要定义多长:
当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size。
当指定类型的数组长度大于了集合的size,那么就不会新创建数组,而是使用传递进来的数组。
所以创建一个刚刚好的数组最优。
2.为什么要将集合变数组:
为了限定对元素的操作。不需要进行增删了。
(集合长度可变,当集合中元素已确定数量时,为防止他人操作改变集合长度,可以返回数组,数组长度固定)
P213
高级 for 循环
格式:
for(数据类型 变量名:被遍历的集合(Collection)或者数组)
对集合进行遍历,只能获取集合元素,但是不能对集合进行操作。
迭代器除了遍历,还可以进行 remove 集合中元素的动作。
如果是用 ListIterator ,还可以在遍历的过程中对集合进行增删改查的动作。
传统for和高级for有什么区别:
高级for有一个局限性,必须有被遍历的目标。
建议在遍历数组的时候,还是希望使用传统for。因为传统for可以定义脚标。
P214
可变参数:如 int... arr
其实就是上一种数组参数(int[] arr)的简写形式,不用每一次都手动的建立数组对象。
只要将要操作的元素作为参数传递即可,隐式的将这些参数封装成了数组。
方法的可变参数:
在使用时注意:可变参数一定要定义在参数列表的最后面。
P215
静态导入:import static java.util.Arrays.*; 导入的是Arrays这个类中的所有静态成员。
当类名重名时,需要指定具体的包名。
当方法重名时,指定具备所属的对象或者类。 如Arrays.toString(...); 与 Object 类中的 toString() 重名,需要指定方法所属类名。
P216
System:类中的方法和属性都是静态的。
out:标准输出,默认是控制台。
in: 标准输入,默认是键盘。
获取系统属性信息:Properties getProperties();
因为 Properties 是Hashtable 的子类,也就是Map集合的一个子类对象。
那么可以通过map的方法取出该集合中的元素。
该集合中存储的都是字符串,没有泛型定义。
在系统中自定义一些特有信息:System.setProperty(key,value); 字符串类型
获取指定属性信息:System.getProperty(key); 若没有该键,则返回null。
在jvm启动时,动态加载一些属性信息:java -D<name>=<value> 要运行的文件
P217
Runtime 对象
该类无构造函数,且方法非静态,但提供了一个静态的返回本类类型对象的方法,来获取本类对象。
该方法是: static Runtime getRuntime();
由该特点看出,该类使用了单例设计模式完成。
Process exec(String command) 可以执行命令。
Process是一个抽象类,不能建立对象,电脑帮助建立进程。
Process中有方法: abstract void destroy() 杀死进程。
P218
Date 对象获取时间是固定格式,如果想要更改格式显示,就要定义 SimpleDateFormat(java.text包下) 对象并在构造时指定格式(字符串形式),
然后调用 format 方法传入日期对象,就能按照自定义格式显示时间。
P219
Date中部分方法已被Calendar(abstract)代替,Calendar 抽象,通过 getInstance 方法获得实例对象。
P220
Math中 ceil():上取整
floor():下取整
round():四舍五入
pow():幂
random():返回带正号的double值,该值>=0.0且<1.0
Random 类也可以通过创建对象后产生随机数。如 r.nextInt();
保留指定位数小数:
方法一:
两个整型数据相除,获得浮点型数据并保留指定小数位数
float percent = (float)54324/(float)345;
// 保留一位乘十再除十,保留两位改为100,以此类推
percent = ((float)Math.round(percent*10))/10;
方法二:
浮点数获得保留指定位数的字符串
float f = 23.872385f;
DecimalFormat df =new DecimalFormat("#0.00");
String result = df.format(f);
P221
流按操作数据分为两种:字节流与字符流。
流按流向分为:输入流,输出流。
Unicode 中无论什么字符都用两个字节表示,一个字节8个二进制位。
字节流的抽象基类:
InputStream OutputStream
字符流的抽象基类:
Reader Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream 的子类 FileInputStream。
如:Reader 的子类 FileReader。
如:FileWriter fw = new FileWriter("..."); 在初始化FileWriter对象的同时明确被操作的文件。
该文件会被创建到指定目录下,且若该目录有同名文件,将被覆盖。
fw.write("..."); 调用write方法,将字符串写入流中。
fw.flush(); 刷新流对象中的缓冲区中的数据。将数据刷到目的地中。
P230
BufferedWriter
缓冲区的出现是为提高流的操作效率。
所以在创建缓冲区之前,必须要先有流对象。
只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
该缓冲区中提供了一个跨平台的换行符。
注意:只要用到缓冲区,就要记得刷新。(flush)
其实关闭缓冲区,就是在关闭缓冲区中的流对象。(close)
P231
BufferedReader
字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法,方便于对文本数据的获取。
当返回 null 时,表示读到文件末尾。
String readLine():读取文件行数据
readLine 方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
P235
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于对象已有的功能,并提供加强功能。
那么可以自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
P236
装饰模式比继承要灵活,避免了继承体系臃肿。
而且降低了类与类之间的关系。
装饰类因为是增强已有对象,具备的功能和已有对象是相同的,只不过提供了更强的功能性。
所以装饰类和被装饰类通常是都属于一个体系中的。
P238
LineNumberReader:
getLineNumber:获取每一行的行号。
setLineNumber:设置开始行号。
本机IP地址:127.0.0.1
常用端口号:
1.80端口:网络端口
如www.baidu.com:80 正确的网址 www.baidu.com:70 错误的端口,打不开。
2.数据库:
mysql:3306 oracle:1521
3.Tomcat服务器:8080
TCP通信:客户端(Socket类)和服务器(ServerSocket类)端必须经过3次握手,建立逻辑连接(包含一个IO流对象),才能通信(安全)
客户端和服务器端进行一个数据交互,需要4个IO流对象。(客户端发送请求,服务器端接收请求,服务器端回复数据,客户端接收数据)
服务器端有一个方法,叫accept客户端获取到请求的客户端对象。
服务器使用客户端的流和客户端交互。
注:1.客户端和服务器端进行交互,必须使用Socket中提供的网络流(getInputStream、getOutputStream),不能使用自己创建的流对象。
2.当我们创建客户端对象Socket的时候(构造方法绑定服务器的IP地址和端口号),就会去请求服务器,和服务器经过3次握手建立连接通路。
这时如果服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
如果服务器已经启动,那么就可以进行交互了。

浙公网安备 33010602011771号