JAVA基础笔记

第三章:数据类型和运算符

定义byte类型变量时,要注意不能超出其范围,不然就需要强转类型。

关于Java中的float数据类型:https://www.zhihu.com/question/46432979/answer/221485161

**只有浮点数*除以0才可以得到正无穷大或负无穷大

关于Java变量在内存中的存储问题

数组:

注意:1,使用foreach循环迭代数组元素时,并不能改变数组元素的值,因此不要对foreach的循环变量进行赋值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cZGfW9QY-1606375471223)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201017101313055.png)]

spliterator是java1.8新提出的能够进行并行遍历的迭代器.

String.valueOf():

Java代码复用(组合和继承)

**注意:**static修饰的成员不能访问没有static修饰的成员

如果在static修饰的方法中使用this关键字,则这个关键字就无法指向合适的对象,所以,static修饰的方法中不能使用this使用

第五章 面向对象

1,同一个类的一个方法调用另外一个方法时,如果被调用方法是普通方法,则默认使用this作为调用者;如果被调方法是静态方法,则默认使用类作为调用者。也就是说,表面上看起来某些方法可以被独立执行,但实际上还是使用this或者类来作为调用者。

2,Java里方法的参数传递方式只有一种:值传递。所谓值传递,就是将实际参数值的副本(复制品)传入方法内,而参数本身不会受到任何影响。

3,形参个数可变的方法,在最后一个形参的类型后增加三点(…)eg.public void test(int a,String… books) 注意:个数可变的形参只能处于形参列表的最后,一个方法中最多只能包含一个个数可变的形参。个数可变的形参本质就是一个数组类型的形参,因此调用包含个数可变形参的方法时,该个数可变的形参既可以传入多个参数,也可以传入一个数组

4 方法重载

5 方法重写:如果父类方法具有private访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也就是无法重写该方法。

5 访问控制级别修饰符:

**private:**如果类里的一个成员(包括成员变量,方法和构造器等)使用private访问控制符来修饰,则这个成员只能在当前类的内部被访问

**default:**包访问权限,可以被相同包下的其他类访问

**protected:**既可以被同一个包中的其他类访问,也可以被不同包中的子类访问。通常情况下,如果使用protected来修饰一个方法,通常是希望其子类来重写这个方法。

**public:**最宽松的访问权限

6,Java的内部类,外部类:内部类是指一个外部类的内部再定义一个类,类名不需要和文件夹相同。

外部类只有两种访问控制级别:public 和 默认

Java设置环境变量的含义:(JAVA_HOME,PATH,CLASSPATH):

7,import lee. 表明导入lee包下的所有类,而lee包下的子包内的类则不会被导入*

8,构造器主要用于被其他方法调用,用以返回该类的实例

9,this和super都不能出现在static修饰的方法中

**多态:**Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态。

​ 与方法不同的是,对象的实例变量不具备多态性。通过引用变量来访问其包含的实例变量时,系统总是试图访问它编译时类型所定义的成员变量,而不是它运行时类型所定义的成员变量。

instance of 注意:instance of 运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则会引起编译错误。

关于引用变量的强制类型转换 引用类型直接的转换这一块不太懂****???****

继承和组合:继承要表达的是is-a的关系,而组合表达的是has-a的关系。

初始化块:*

普通初始化块*:创建对象时隐式调用

静态初始化块:类加载时隐式调用

系统在类初始化阶段执行静态初始化块,而不是在创建对象时才执行。

面向对象(下)

​ 1,程序判断两个引用变量是否相等时,也希望有一种类似于“值相等”的判断规则。并不严格要求两个引用变量指向同一个对象。此时可用equals()

2,类成员

​ static关键字修饰的成员就是类成员,static关键字不能修饰构造器

​ 对static关键字而言,有一条非常重要的规则,类成员(包括方法,初始化块,内部类,枚举类)不能访问实例成员(包括成员变量,方法,初始化块,内部类和枚举类)

3,单例(Singleton)类:

​ 如果一个类始终只能创建一个实例,则这个类被称为单例类

4,final修饰符:final关键字可用于修饰类,变量和方法,它修饰的类,方法和变量不可改变。

final修饰的成员变量必须由程序员显式地指定初始值。

1,不要再初始化之前直接访问final成员变量,但Java又允许通过方法来访问final成员变量,此时系统将成员变量默认初始化为0(或"\u0000",false或null)

2,系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。

3,不能对final修饰的形参赋值

4,final修饰符是一个重要作用就是定义“宏变量”

5,final修饰的方法不可被重写,如果不希望子类重写父类的某个方法,则可以使用final修饰该方法

​ final修饰的方法仅仅是不能被重写,并不是不能被重载。

不可变类:

1,不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的,Java提供的包装类和java.lang.String类是不可改变类。

2,Integer只缓存-128-127之间的对象

抽象类:

1,抽象方法是只有方法签名,没有方法实现的方法。抽象方法和抽象类必须使用abstract修饰符来修饰,抽象类里可以没有抽象方法。

2,抽象类不能被实例化

3,抽象类的构造器不能用于创建实例,主要用于被其子类调用。

4,abstract不能用于修饰成员变量,局部变量,构造器。

5,static和abstract不能同时修饰某个方法,没有所谓的类抽象方法。static和abstract并不是绝对互斥的,static和abstract虽然不能同时修饰某个方法,但它们可以同时修饰内部类。

6,抽象类体现的是一种模板模式的设计。

接口:

1,接口定义了一种规范,接口定义了某一批类所需要遵守的规范。接口是从多个相似类中抽象出来的规范,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学。接口通常是定义一组公用方法。

2,[修饰符] interface 接口名 extends 父接口1,父接口2…

{

零到多个常量,抽象方法,内部类,接口,枚举,私有方法,默认方法或类方法…

}

修饰符可以是public或者省略,如果省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才可以访问接口。

3,由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法,默认方法或私有方法),内部类(包括内部接口,枚举)定义。

4,对于接口里定义的静态常量而言,系统会自动为这些成员变量增加static和final修饰符,也就是说,在接口中定义成员变量时,不管是否使用public static final修饰符,接口里的成员变量总是使用这三个修饰符来修饰。

5,接口里的普通方法不能有方法实现(实现体);但类方法,,默认方法,私有方法都必须有方法实现(方法体)

6,接口里定义的内部类,内部接口,内部枚举默认采用public static两个修饰符,不管定义时是否指定这两个修饰符,系统都会自动使用这两个修饰符对它们进行修饰。

7,默认方法必须使用default修饰,该方法不能使用static修饰。默认方法总是使用public修饰。需要使用接口的实现类的实例来调用这些默认方法。

8,类方法必须使用static修饰,该方法不能使用default修饰。类方法总是使用public修饰。类方法可以直接使用接口来调用。

9,一个Java源文件中最多只能有一个public接口,所以一个Java源文件中定义了一个public接口,则该源文件必须与该接口名相同。

10,接口完全支持多继承,接口不能用于创建实例,但接口可以用于声明引用类型变量。

11,一个类可以实现一个或多个接口,使用implements关键字,implements部分必须放在extends部分后面。

12,一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。

java中的System.copy(),:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fcxZiVjb-1606375471225)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201022144154904.png)]

问题:

1,P157页最上面画问号的地方

2,关于构造器和静态变量的问题

内部类:

1,大部分时候,类被定义成一个独立的程序单元,在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类的内部的类就称为内部类(有的地方也叫宿主类。)

2,内部类比外部类可以多使用三个修饰符:private,protected,static----外部类不可以使用这三个修饰符。

2,非静态内部类不能拥有静态成员

3,外部类的静态方法,静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量,创建实例。

4,Java不允许在非静态内部类里定义静态成员。非静态内部类里不能有静态方法,静态成员变量,静态初始化块。

静态内部类:

使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,有的地方也称为静态内部类

1,静态内部类可以包含静态成员,也可以包含非静态成员,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。

2,接口里也可以定义内部类,接口里定义的内部类默认使用public static修饰,也就是说,接口内部类只能是静态内部类

局部内部类:

1,如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类。如果需要在局部内部类定义变量,创建实例或派生子类,那么都只能在局部内部类所在的方法内进行。

匿名内部类:

1匿名内部类适合创建那种只需要一次使用的类,例如前面介绍命令模式时所需要的Command对象。

2,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。

3,定义格式:

new 实现接口()| 父类构造器(实参列表){

​ 匿名内部类的类体部分

}

4,匿名内部类不能是抽象类; 匿名内部类不能定义构造器

5,当通过接口来创建匿名内部类时,匿名内部类不能显式创建构造器,因此匿名内部类只有一个隐式的无参数构造器,故new接口名后的括号里不能传入参数值;但如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似是指拥有相同的形参列表。

Java8新增的Lambda表达式:

1,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。

2,Lambda表达式的主要作用就是代替匿名内部类的繁琐语法,由三部分组成:形参列表,

箭头(->),代码块

3,Lambda表达式的类型,也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法,但只能声明一个抽象方法。

4,匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法,但Lambda表达式的代码块不允许调用接口中的默认方法

**方法引用与构造器引用

为什么Java中匿名内部类访问的局部变量只能是final**:https://blog.csdn.net/tianjindong0804/article/details/81710268

枚举类:

1,定义:在某些情况下,一个类的对象是有限而固定的,比如季节类,它只有四个对象,再比如行星类,目前只有8个对象。这种实例有限而且固定的类,在Java里被称为枚举类。

2,使用enum关键字(它与class,interface关键字的地位相同)用以定义枚举类

3,枚举类是一种特殊的类,它一样可以有自己的成员变量,方法,可以实现一个或者多个接口,也可也定义自己的构造器。一个Java源文件最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。

4,使用enum定义,非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类

5,枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰

6,枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final修饰,无须程序员显示添加

7,枚举类默认提供了一个values()方法,该方法可以很方便的遍历所有的枚举值。EnumClass.values()。。使用枚举实例时,可通过EnumClass.variable形式来访问

8,一旦为枚举类显式定义了带参数的构造器,列出枚举值时就必须对应的传入参数

eg, public enum Gender{

MALE("男"),FEMALE("女");

private final String name;



private Gender(String name){

​      this.name = name;   

}   

public String getName(){

​    return this.name;

}

}
垃圾回收:

1,垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源(例如数据库连接,网络连接等资源)

2,在垃圾回收机制回收任何对象之前,系统会调用所有可恢复对象的finalize()方法进行资源清洗。总该方法可能使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消回收

3,程序无法精确控制Java垃圾回收的时机,但依然可以强制系统进行垃圾回收----这种强制只是通知系统进行垃圾回收,但系统是否进行垃圾回收依然不确定。大部分的时候,程序强制系统垃圾回收后总会有一些效果。有两种方式:

​ 1,调用系统System类的gc()静态方法:System.gc()

​ 2,调用Runtime对象的gc()实例方法:Runtime.getRuntime().gc()

Java中的命令行参数 -verbose 使编译器和连接器输出关于正在编译哪些类和正在加载哪些类文件的信息。

对象的软,弱,虚引用:

1,java.lang.ref包下提供了三个类:SoftReference,PhantomReference,WeakReference,分别代表了软引用,虚引用和弱引用

2,强引用:程序创建一个对象,并把这个对象赋给一个引用变量,这是最常见的

3,虚引用必须和引用队列(java.lang.ref.ReferenceQueue)类表示,引用队列用于保存被回收后对象的引用。

4,软引用和弱引用可以单独使用,但虚引用不能单独使用。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含该虚引用,从而了解虚引用所引用的对象是否被立即回收。

5,使用这些引用类可以避免在程序执行期间将对象留在内存中,如果以软引用,弱引用或者虚引用的方式引用对象,垃圾回收器就能够随意的释放对象。如果希望尽可能减小程序在其生命周期所占的内存时,这些引用类就很有用处。

6,要使用这些引用类,就不能保留对对象的强引用。如果保留了对对象的强引用,就会浪费这些引用类所提供的任何好处。

Java中的修饰符:

1,包访问控制符是一个特殊的修饰符,不用任何访问控制符的就是包访问控制符,对于初始块和局部成员而言,它们不能使用任何访问控制符,所以看起来像是使用了包访问控制符。

2,strictfp关键字的含义是FP-strict,也就是精确浮点的意思。一旦使用了strictfp来修饰类,接口或者方法时,那么在所修饰的范围内,Java编译器和运行时环境会完全依照浮点规范IEEE-754来执行。

3,native关键字主要用于修饰一个方法,使用native修饰的方法类似于一个抽象方法,不同的是native方法通常采用C语言来实现。如果某个方法需要利用平台相关特性,或者访问系统硬件等,则可以使用native修饰该方法,再把该方法交给C去实现,一旦Java程序中包含了native方法,个程序将失去跨平台的功能

4,Java还允许在接口里定义内部类,接口里定义的内部类默认使用public static 修饰,也就是说,接口内部类只能是静态内部类。

5,abstract和final永远不能同时使用;abstract和static不能同时修饰方法,可以同时修饰内部类;abstract和private不能同时修饰方法,可以同时修饰内部类

第7章:Java基础类库

1> Scanner:

​ 1,使用Scanner类可以很方便地获取用户的键盘输入,Scanner是一个基于正则表达式的文本扫描器,它可以从文件,输入流,字符串中解析出基本类型值和字符串值。

2,Scanner不仅能读取用户的键盘输入,还可以读取文件输入。只要在创建Scanner对象时传入一个File对象作为参数。

2> 系统相关:Java提供了System类和Runtime类来与程序的运行平台进行交互

1,System类代表当前Java程序的运行平台,程序不能创建System类的对象,System类提供了一些类变量和类方法,允许直接通过System类来调用这些类变量和类方法

2,Runtime类代表Java程序的运行时环境,每个Java程序都有一个与之对应的Runtime实例,应用程序通过该对象与其运行时环境相连。应用程序不能创建自己的Runtime实例,但可以通过getRuntime()方法获取与之关联的Runtime对象

3>常用类

1 Object类:是所有类,数组,枚举类的父类,也就是说,Java允许把任何类型的对象赋给Object类型的变量。

​ 常用的方法:boolean equals(Object obj)

​ protected void finalize()

​ Class<?> getClass() :返回该对象的运行时类

​ int hashCode()

​ String toString():返回该对象的字符串表示·

2,Objects类:提供了一些工具方法来操作对象,这些工具方法大多是“空指针”安全的。eg.比如你不能确定一个引用变量是否为null,如果贸然调用该变量的toString()方法,则可能引发NullPointerExcetpion异常,如果使用Objects类提供的toString(Object o)方法,就不会引发空指针异常,当o为null时,程序将返回一个null字符串。

requireNonNull()方法:当传入的参数不为null时u,该方法返回参数本身,否则会引发NullPointerExcetpion异常

​ 1,Java为工具类的命名习惯是添加一个字母s,比如操作数组的工具类Arrays,操作集合的工具类是Collections。

3 String ,StringBuffer和StringBuilder类:这三个类是用来封装字符串,并提供一系列方法来操作字符串对象。

​ 1> StringBuilder,StringBuffer的用法基本相同,只是StringBuffer是线程安全的

4 Math类:这是一个工具类,它的构造器被定义成private的,因此无法创建Math类对象.Math类中所有的方法都是类方法,可以直接通过类名来调用他们。此外,Math类还提供了两个类变量,PI和E

上溢和下溢

5 ThreadLocalRandom 和 Random类

Random类专门用于生成一个伪随机数,它有两个构造器:一个构造器使用默认的种子(以当前时间作为种子),另一个构造器需要程序员显式传入一个long型整数的种子。

ThreadLocalRandom类是Random类的增强版,在并发访问的环境下,使用ThreadLocalRandom来代替Random可以减少多线程资源竞争,最终保证系统具有更好的线程安全性。

种子:用于随机数生成器的初始化值,随机数生成器对于特定的种子值总是产生相同的随机数序列

​ 3,Random使用一个48位的种子,如果这个类的两个实例是用一个种子创建的,对它们以同样的顺序调用方法,则它们会产生相同的数字序列

6 BigDecimal类:为了能精确表示,计算浮点数,Java提供了BigDecimal类

​ 1,创建BigDecimal对象时,不要直接使用double浮点数作为构造器参数来调用BigDecimal构造器,而是使用String对象作为构造器参数。即参数要加引号,如”0.05“

7,日期,时间类

1> Date类:处理日期,时间类 Java官方推荐尽量少用Date的构造器和方法

2> Calendar类:因为Date类在设计上存在一些缺陷,所以Java提供了Calendar类来更好地处理日期和时间。Calendar是一个抽象类,它用于表示日历。

​ 1,set()方法延迟修改

8,正则表达式:一个强大的字符串处理工具,可以对字符串进行查找,提取,分割,替换等操作。

1,“通配符”是可以匹配多个字符的特殊字符,正则表达式中的通配符远远超出普通通配符的功能,它被称为预定义字符。

2,正则表达式支持的数量标识符有如下几种模式

Greedy(贪婪模式) Reluctant(勉强模式) 用?后缀表示 **Possessive(占有模式)**用+后缀表示

3,一旦在程序中定义了正则表达式,就可以使用Pattern和Matcher来使用正则表达式

4,Pattern对象是正则表达式编译后在内存中的表达形式,因此,正则表达式字符串必须先被编译为Pattern对象,然后再利用该Pattern对象创建对应的Matcher对象。执行匹配所涉及到状态保留在Matcher对象中,多个Matcher对象可共享同一个Pattern对象

第8章 Java集合

1 Java集合大致可分为Set,List,Queue和Map四种体系,其中Set代表无序,不可重复的集合;List代表有序,重复的集合;而Map则代表具有映射关系的集合,Java5又增加了Queue体系的集合,代表一种队列集合实现。

2,集合类主要负责保存,盛装其他数据,因此集合类也被称为容器类。所有的集合类都位于java.util包下

3,集合类和数组不同,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合里只能保存对象(实际上只是保存对象的引用变量。但通常习惯上认为集合里保存的是对象)

4,Java集合类主要由两个接口类派生而出:Collection和Map,这两个是Java集合框架的根接口

5 , Collection接口中定义的一些方法: Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。Iterator对象也被称为迭代器。Object next()方法返回集合里的下一个元素,注意返回的数据类型是Object类型,因此要注意类型转换。

6, Iterator必须依附于Collection对象,若有一个Iterator对象,则必然有一个与之关联的Collection对象。当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何影响。只有通过Iterator的remove()方法删除上一次next()方法返回的集合元素才可以。

7, 关于Iterator在迭代删除元素时抛异常

8,Java8新增了一个removeIf(Predicate filter)方法,该方法将会批量删除符合filter条件的所有元素

9,getAsInt():返回OptionalInt对象中存在的值

10,**IntStream.of()**填充一个或多个int元素构造流

11 Java8中的Optional类

Set集合

1, Set集合不允许包含相同的元素。

1> HashSet类:

1, 是Set接口的典型实现,大多数时候使用Set集合时就是使用这一个实现类。HashSet按Hash算法来存储集合中的元素。

​ 2,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

​ 3,HashSet中每个能存储元素的”槽位“通常称为”桶“,如果多个元素的hashCode值相同,但他们通过equals()方法比较返回false,就需要在”桶"里放多个元素,这样会导致性能下降.

​ 4, 当程序把可变对象添加到HashSet中之后,尽量不要去修改该集合元素中参与计算的hashCode(),equals()的实例变量,否则会导致HashSet无法正确操作这些集合元素。

2> LinkedHashSet类 :HashSet的子类,其也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序。因为其需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问全部元素时具有很好的性能。

3> TreeSet类:SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态。

问题**:P307页

​ 1,TreeSet采用红黑树的数据结构来存储集合元素。

​ 2,TreeSet支持两种排序方法:自然排序和定制排序,在默认情况下,TreeSet采用默认排序。

​ 3,如果试图把一个对象添加到TreeSet时,该对象的类必须实现Comparable接口,否则程序将会抛出异常。大部分类在实现compareTo(Object obj)方法时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同类型的两个实例才会比较大小。所以,向TreeSet中添加的应该是同一个类的对象,否则也会引发异常。

​ 4,总结:如果希望TreeSet能正常运作,TreeSet只能添加同一种类型的对象

​ 5,定制排序:如果需要实现定制排序,则需要在创建TreeSet集合对象时,提供一个Compartor(该接口包含一个int compare(T o1,T o2))对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。由于Compartor是一个函数式接口,因此可以使用Lambda表达式来代替Comparator对象。

4> EnumSet类:是一个专门为枚举类设计的集合类。该集合中的所有元素都必须是指定枚举类型的枚举值,该集合中的元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序 。

​ 1,EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建EnumSet对象

各Set实现类的性能分析

1 ,HashSet和TreeSets是Set的两个典型实现。HashSet的性能总是比TreeSet好(特别是最常用的添加,查询元素等操作)。因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet

2, EnumSet是所有Set实现类中性能最好的,但它只保存同一个枚举类的枚举值作为集合元素

3,Set的三个实现类都是线程不安全的,必须手动来保证集合的同步性。

List集合:

1,代表一个元素有序,可重复的集合,集合中的每个元素都有其对应的顺序索引。可通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。

2,与Set只提供一个iterator()方法不同,List还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法

ArrayList和Vector实现类:是List类的两个典型实现,都是基于数组实现的List类,所以二者封装了一个动态的,允许再分配的Object[]数组

​ 1> 二者的显著区别:ArrayList是线程不安全的 ,而Vector是线程安全的

​ 2> Vector还提供了一个Stack子类,用于模拟“栈”这种数据结构

3> Arrays,该工具类提供了asList(Object… a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个集合是Arrays的内部类ArrayList的实例。Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加,删除该集合里的元素。

Queue集合:

Queue用于模拟队列这种数据结构,队列通常是指“先进先出”(FIFO)的容器。访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。

1>PriorityQueue实现类:其保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序,所以要取元素时,先取出的是队列中最小的元素。

​ 1,PriorityQueue不允许插入null元素,它还需要对队列元素进行排序,有两种排序方式。自然排序和定制排序。(与TreeSet要求基本一致)

**2> Deque接口与ArrayDeque实现类:**Deque接口是Queue接口的子接口,它代表一个双端队列,Deuqe接口里定义了一些双端队列的方法,这些方法允许从两端来操作队列。

​ 1,Deque接口提供了一个典型的实现类:ArrayDeque,是基于数组实现的双端队列。

​ 2,ArrayDeque不仅可以作为栈使用,也可以作为队列使用

3>LinkedList实现类:该类是List接口的实现类----这意味着它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外,该类还实现了Deque接口,可以被当成双端队列来使用。

对于所有内部基于数组的集合实现,例如ArrayList,ArrayDeque等,使用随机访问的性能比使用Iterator迭代访问的性能要好,因为随机访问会被映射成对数组元素的访问。

Map集合:
用于保存具有映射关系的数据,一组值用于保存Map里的key,另外一组用于保存Map里的value

​ 1> Map的这些实现类和子接口中key集的存储形式和对应Set集合中元素的存储形式完全相同

​ 2> Map的内部类Entry

​ 3> HashMap和Hashtable是Map接口的典型实现类。Hashtable是一个古老的Map实现类,是一个线程安全的Map实现,而Hashtable是线程不安全的实现。

​ 4>Hashtable不允许使用null作为key和value,但HashMap可以使用null作为key或value

​ 5>为了成功地在HashMap,Hashtable中存储,获取对象,用作key的对象必须实现hashCode()方法和equals()方法

​ 6>HashMap,Hashtable判断两个key相等的标准,equals()方法比较返回true,两个key的hashCode值也相等。 判断两个value相等的标准,equals()方法比较返回true。

​ 7>Properties类是Hashtable类的子类,该对象在处理属性文件时特别方便,该类可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的“属性名–属性值”加载到Map对象中。

SortedMap接口和TreeMap实现类

1> TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序。

**WeakHashMap实现类:**与HashMap的用法基本相似,只不过HashMap的key保留了对实际对象的强引用,而WeakHashMap的key只保留了对实际对象的弱引用。当垃圾回收了该key所对应的实际对象之后,WeakHashMap会自动删除该key对应的key-value对

IdentityHashMap类:该类的实现机制与HashMap基本类似,但它在处理两个key相等时比较独特,当且仅当两个key严格相等(key1 == key2)时,才认为这两个key相等。

EnumMap实现类:是一个与枚举类一起使用的Map实现。该类中所有的key都必须是单个枚举类的枚举值。在内部以数组形式保存,所以这种实现形式非常紧凑,高效。

hash表中“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:较高的负载极限可以降低hash表所占用的内存空间,但会增加查询数据的时间开销;较低的负载极限会提高查询数据的性能,但会增加hash表所占用的内存开销。

操作集合的工具类:

​ Collections Java提供的一个操作Set,List和Map等集合的工具类,该工具类提供了大量方法对集合元素进行排序,查询和修改等操作,还提供了将集合对象设置为不可变,对集合对象实现同步控制等方法。

1> java中,方法参数为(String… names)这个是参数不确定个数的定义说明,是JAVA泛型的一种体现,原理与(String[] names)基本一致

2>同步控制:Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

**3>设置不可变集合:**Colllections提供了三类方法来返回一个不可变的集合。P339页。不可变意味着程序不能向集合中添加元素,也不能从集合中删除元素

第九章 泛型

1> 增加泛型支持在很大程度上都是为了让集合能记住其元素的数据类型,在没有泛型之前,一旦把一个对象“丢进”Java集合中,集合就会忘记对象的类型,把所有对象当成Object类型处理。当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅使代码臃肿,而且容易引起ClassCastException异常。

2> 增加了泛型支持后的集合,完全可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误。

3> Integer等数据类型不能强制转换成String类型

4> 从Java5之后,Java引入了“参数化类型”的概念,Java的参数化类型被称为泛型(Generic)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Midl8KuR-1606375471226)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201101140655962.png)]

“菱形语法”:在Java7之前,如果使用带泛型的接口,类定义变量,那么调用构造器创建对象时构造器的后面也必须带泛型,这显得有些多余了

​ 如:List strList = new ArrayList();

从Java7开始,Java允许在构造器后不需要带完整的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断尖括号里应该是什么泛型信息

​ 如:List strList = new ArrayList<>();

<>并排放在一起像菱形,这种语法也被称为“菱形”语法

5> 所谓泛型,就是在允许在定义类,接口,方法时使用类型形参,这个类型形参(或叫泛型)将在声明变量,创建对象,调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)

6>可以为任何类,接口增加泛型声明(并不是只有集合类才可以使用泛型声明)

7> 当创建了带泛型声明的接口,父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口,父类时不能在包含泛型形参。

8>调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类,接口时也可以不为泛型形参传入实际的类型参数。eg. public class A extends Apple , 像这种使用Apple类时省略泛型的形式被称为原始类型(raw type)

9> 在静态方法,静态初始化块或者静态变量的声明和初始化中不允许使用泛型形参。https://www.cnblogs.com/zhuomuniao/p/12380766.html

10> 系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类

11>如果Foo是Bar的一个子类型(子类或者子接口),而G是具有泛型声明的类或者接口,G并不是G的子类型!!!

12> Java泛型的设计原则是,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException异常

13> 数组和泛型有所不同,假设Foo是Bar的一个子类型(子类或者子接口),那么Foo[]依然是Bar[]的子类型;但G并不是G的子类型!!!

类型通配符:为了表示各种泛型List的父类,可以使用类型通配符,类型通配符是一个问号(?),例如List<?>,意思是元素类型未知的List,.

1> 带通配符的List仅表示它是各种泛型List的父类,并不能把元素加入到其中。唯一的例外是null,它是所有引用类型的实例

2> 限制的通配符:

​ 1, List<? extends Shape>,这种指定通配符上限的集合,只能从集合中取元素(取出的元素总是上限的类型),不能向集合中添加元素(因为编译器没法确定集合元素实际上是哪种子类型)

​ 2,指定通配符上限就是为了支持类型型变,这种型变方式称为协变。

​ 3,<? super 类型>,通配符的下限,其作用与通配符的上限的作用恰好相反。指定下限也是为了支持类型型变,这种型变方式被称为逆变。

​ 4,对于逆变的泛型集合来说,编译器只知道集合元素是下限的父类型,但具体是哪种父类型则不确定。因此,这种逆变的泛型集合只能向其中添加元素(因为实际赋值的集合元素总是逆变声明的父类),从集合中取元素时只能被当作Object类型处理(编译器无法确定取出的到底是哪个父类的对象)

​ 5,Java泛型不仅允许在使用通配符形参时设定上限,而且可以在定义泛型形参时设定上限,用于表示传给该泛型形参的实际类型要么是该上限类型,要么是该上限类型的子类。

问题:在静态方法中不允许使用静态形参???

泛型方法:

在声明方法时定义一个或者多个泛型形参

1>语法格式:

修饰符 <T,S> 返回值类型 方法名(形参类表)

泛型方法和类型通配符的区别:???不太懂

1,泛型形参T产生的唯一效果是可以在不同的调用点传入不同的实际类型。对于这种情况,应该使用通配符,通配符就是被设计用来支持灵活的子类化的。

2,泛型方法允许泛型形参被用来表示方法的一个或者多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这样的依赖关系,就不应该使用泛型方法。

3,变量的显式/隐式声明

4,类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的泛型形参必须在对应方法中显式声明 ????

第十章:异常处理

1,Java将异常分为两种,Checked异常和Runtime异常,Java认为Checked异常都是可以在编译阶段被处理的异常,所以它强制程序处理所有的Checked异常,而Runtime异常则无需处理。

两种异常的区别

2,Java的异常处理机制可以让程序具有极好的容错性,让程序更加健壮。当程序运行出现意外情形时,系统会自动生成一个Exception对象来通知程序,从而实现将“业务功能实现代码”和“错误处理代码”分离,提供更好的可读性。

使用try…catch捕获异常:把系统的业务实现代码放在try块中定义,所有的异常处理逻辑放在catch块中进行处理。

​ 1,try块和catch块后的花括号{}不可以省略,还有一点,try块里声明的变量是代码块内局部变量,它只在try块内有效,在catch块中不能访问该变量。

3,从Java7开始,一个catch块可以捕获多种类型的异常,多种异常类型之间用 竖线| 隔开,捕获多种异常时,异常变量有隐式的final修饰,因此不能对异常变量重新赋值

**使用finally回收资源:**有些时候,程序在try块里打开一些物理资源(例如数据库连接,网络连接和磁盘文件等)。这些物理资源都必须显式回收。**注意:**Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存中对象所占用的内存。

​ 1,在通常情况下,不要在finally块中使用如return或throw等导致方法终止的语句。一旦在finally块中使用了return或throw语句,将会导致try块,catch块中的return,throw语句失效。

使用throws声明抛出异常:

​ 使用throws声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理;如果main方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理。JVM对异常的处理方法是,打印异常的跟踪栈信息,并中止程序的运行。

​ 使用throw抛出异常:当程序出现错误时,系统会自动抛出异常;除此之外,Java也允许程序自行抛出异常,自行抛出异常使用throw语句来完成。

1,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。

异常链:https://www.cnblogs.com/zhihaospace/p/12228912.html
Java的异常跟踪栈:

异常对象的printStackTrace()用于打印异常的跟踪栈信息

**异常处理规则:**成功的异常处理应该实现如下4个目标:

​ 1,使程序代码混乱最小化

​ 2,捕获并保留诊断信息

​ 3,通知合适人员

​ 4,采用合适的方式结束异常活动

第十三章:MySQL数据库与JDBC编程

​ 1,JDBC全称为Java Database Connectivity,即Java数据库连接,它是一种可以执行SQL语句的Java API

​ 2,Sun提供的JDBC可以完成以下三个基本工作:

​ 建立与数据库的连接

​ 执行SQL语句

​ 获得SQL语句的执行结果

​ 3,通常所说的数据库既包括存储用户数据的部分,也包括管理数据库的管理系统

​ 4,数据库管理系统,简称DBMS,是所有数据的知识库,它负责管理数据的存储,安全,一致性,并发,恢复和访问等操作。

​ 5,MySQL数据库通常支持如下两种存储机制:

​ 1,MyISAM:这是MySQL早期的存储机制,对事务支持不够好。

​ 2,InnoDB:提供事务安全的存储机制。InnoDB通过建立行级锁来保证事务完整性,并以Oracle风格的共享锁来处理Select语句。

InnoDBMyISAM多了事务支持的功能,而事务支持是JavaEE最重要的特性,因此推荐使用InnoDB

6,标准的SQL语句通常分为如下几种类型:

1,查询语句:select

2,DML(Data Manipulation Language,数据操作语言):insert,update和delete

3,DDL(Data Definition Language,数据定义语言):create,alter,drop和truncate

4,DCL(Data Control Language,数据控制语言):grant,revoke

5,事务控制语句:commit,rollback和savepoint

SQL语句的关键字不区分大小写

从MySQL4.1开始,**varchar (N)**中的N指的是该字段最多能存储多少个字符(characters),不是字节数。不管是一个中英文字符或者数字、或者一个汉字,都当做一个字符。在4.1之前,N表示的是最大存储的字节数(bytes)。

7,MySQL的修改语句modify命令不支持一次修改多个列定义。如果修改数据列的默认值,则只会对以后的插入操作有作用,对以前已经存在的数据不会有任何影响。

**8,truncate命令:**truncate都被当成DDL处理,truncate被称为“截断“某个表----它的作用是删除该表里的全部数据,但保留表结构。相对于DML里的delete命令而言,truncate的速度要快得多。truncate不像delete可以删除指定的记录,truncate只能一次性删除整个表的全部记录。

数据库约束**:
1,MySQL不支持CHECK约束,虽然MySQL的SQL语句也可以使用CHECK约束,但这个CHECK约束不会有任何作用。

2,约束也是数据库对象,并被存储在表中,也拥有自己的名字,根据约束对数据列的限制,约束分为如下两类:1,单列约束,每个约束只约束一列 2,多列约束:每个约束可以约束多个数据列

3,NOT NULL约束:指定某列不能为空

4,**UNIQUE约束:**唯一约束,指定某列或者几列组合不能重复。虽然唯一约束的列不可以出现重复值,但可以出现多个null值(因为在数据库中null不等于null)

​ 对于大部分数据库而言,删除约束都是在alter table语句后使用”drop constraint 约束名“语法来完成的,但MySQL并不使用这种方式,而是使用”drop index 约束名“的方式来删除约束。

5,**主键约束:**主键约束相当于非空约束和唯一约束,即主键约束的列即不允许出现重复值,也不允许出现null值;如果对多列组合建立主键约束,则多列里包含的每一列都不能为空,但只要求这些组合不能重复。

​ 每一个表中最多允许有一个主键,但这个主键约束可由多个数据列组合而成,主键是表中能唯一确定一行记录的字段或字段组合。

表级约束和列级约束的区别

**6, 外键约束:**外键约束主要用于保证一个或者两个数据表之间的参照完整性,外键是构建于一个表的两个字段或者两个表的两个字段之间的参照关系。

**7,索引:**索引是存放在模式中的一个数据库对象,虽然索引总是从属于数据表,但它和数据表一样属于数据库对象。创建索引唯一作用就是加速对表的查询。索引通过使用快速路径访问方法来快速定位数据,从而减少了磁盘的I/O。

8,select语句:当使用select语句进行查询时,还可以在select语句中使用算术运算符(+,—,*,/)

9,MySQL中使用concat函数来进行字符串连接运算,对于mysql而言,如果在算术表达式中使用null,将会导致整个算术表达式的返回值都为null,如果在字符串连接运算中出现null,将会导致连接后的结果也是null。

10,起别名:为数据列或表达式起别名时,别名紧跟数据列,中间以空格隔开;或者使用as关键字隔开。如果列别名中使用特殊字符(如空格),或者需要强制大小写敏感,都可以通过为别名添加双引号来实现。

11,select默认会把所有符合条件的记录全部选出来,即使两行记录完全一样。如果想去去除重复行,可以使用distinct关键字从查询结果中清除重复行。distinct紧跟select关键字。

12,SQL中判断两个值是否相等的比较运算符是单等号,判断不相等的运算符是<>,赋值运算符是:=

13,在模糊查询中需要使用like关键字,SQL语句中有两个通配符,下划线_(代表任意一个字符)和百分号%(代表任意多个字符)

14,Mysql中使用反斜线\作为转义字符

15,is null用于判断某些值是否为空

16 排序:order by关键字,默认是升序排列(asc),若要用降序排列,使用关键字desc

**17,数据库函数:分为单行函数和多行函数,单行函数对每行的输入值单独计算,每行得到一个计算结果返回给用户;多行函数(也被称为聚集函数,分组函数)对多行输入值整体计算,最后只会得到一个结果。*,主要用于完成一些统计功能,在大部分数据库中基本相同,但不同数据库中的单行函数差别非常大。

18,分组:group by 。如果需要对分组进行过滤,应该使用having子句

**19,集合运算:**select语句查询的结果是一个包含多条数据的结果集,类似于数学里的集合,可以进行交(intersect),并(union)和差(minus)运算。

​ 语法格式: select 语句 union/minus/intersect select 语句

Mysql不支持intersect和minus

JDBC的典型用法:

1,DriverManager:用于管理JDBC驱动的服务类。程序中使用该类的主要功能是获取Connection对象,该类包含getConnection方法,获得url对应数据库的连接。

2,Connection:代表数据库连接对象,每个Connection代表一个物理连接会话,要想访问数据库,必须先获得数据库连接。该接口的常用方法:createStatement(),返回一个Statement对象。

3,只有获得了Statement对象之后才可执行SQL语句。

4,Statement:用于执行SQL语句的工具接口,该对象既可用于执行DDL,DCL语句,也可用于执行DML语句,还可用于执行SQL查询。

5,PreparedStatement:预编译的Statement对像,它允许数据库预编译SQL语句(这些sql语句通常带有参数),以后每次只改变SQL命令的参数,避免数据库每次都需要编译SQL语句。

JDBC编程步骤:1,加载数据库驱动 例如:Class.forName(“com.mysql.jdbc.Driver”)

​ 2,通过DriverManager获取数据库连接。当使用DriverManager获取数据库连接 时,通常需要传入三个参数:数据库URL。登录数据库的用户名和密码。URL:jdbc:mysql://hostname:port/databasename

​ 3, 通过Connection对象创建Statement对象。

​ 4,使用Statement执行SQL语句。

​ 5,操作结果集。

​ 6,回收数据库资源,包括关闭ResultSet,Statement和Connection等资源

Java中自动关闭资源的try语句

关于JDBC中的getInt(1)

关于数据库中的元数据

6,JDBC中,若两条SQL语句结构基本相似,只是执行插入时插入的值不同而已,对于这种情况,可以使用占位符(?)参数的SQL语句来代替它。

7,使用PreparedStatement的执行效率比Statement高。此外,使用PreparedStatement还有一个优势,当SQL语句中要使用参数时,无须“拼接”SQL字符串,而使用Statement则要“拼接“SQL字符串。还有一点就是PreparedStatement可以防止SQL注入,安全性更好。

8 sql字符串拼接单引号的处理

管理结果集:

1,JDBC使用ResultSet来封装执行查询得到的结果,然后通过移动ResultSet的记录指针来取出结果集的内容。除此之外,JDBC还允许通过ResultSet来更新记录,并提供了ResultSetMetaData来获得ResultSet对象的相关信息。

2,关于ResultSet中的滚动,更新问题

3,可以通过ResultSetMetaData来获取关于ResultSet的描述信息。

4, RowSet接口继承了ResultSet接口,RowSet接口下包含jdbcRowSet,CachedRowSet,FilteredRowSet,JoinRowSet和WebRowSet常用子接口。除jdbcRowSet需要保持与数据库的连接之外,其余四个子接口都是离线的RowSet,无须保持与数据库的连接。
5,与ResultSet相比,RowSet默认是可滚动,可更新,可序列化的结果集。

事务:

​ 事务是由一步或者几步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。程序和事务是两个不同的概念。一般而言,一段程序中可能包含多个事务。

1,事务具备4个特性:原子性(Atomicity),一致性(Consistency),隔离性(Isolation)和持续性(Durability)。这四个特性也简称ACID特性。

2,数据库的事务由下列语句组成

​ 1,一组DML语句,经过这组DML语句修改后的数据将保持较好的一致性

​ 2,一条DDL语句

​ 3,一条DCL语句

​ DDL和DCL语句最多只能有一条,因为DDL和DCL语句都会导致事务立即提交。

3,事务提交有两种方式:显式提交:使用commit

自动提交:执行DDL或DCL语句,或者程序正常退出

4,当事务所包含的任意一个数据库操作执行失败后,应该回滚(rollback)事务,使该事务做的修改全部失效。事务回滚有两种方式:显式回滚:rollback

自动回滚:系统错误或者强行退出

5,如果只是想临时性地开启事务,则可以使用MySql提供的start transaction或begin两个命令。它们都表示临时性地开启一次事务。

6,MySQL还提供了savepoint来设置事务的中间点,通过使用savepoint设置事务的中间点可以让事务回滚到指定的中间点,而不是回滚全部事务。eg.savepoint a;

​ rollback to a;

7,普通的提交,回滚都会结束当前事务。但回滚到指定中间点因为依然处于事务之中,所以不会结束当前事务。

8, JAVA8增强的批量更新:执行executeLargeBatch()方法将返回多个long值,多个long值就组成了这个long[]数组,如果在批量更新的addBatch()方法中添加了select查询语句,程序将直接出现错误。

9,ResultSetMetaData:通过其来获取关于ResultSet的描述信息

DatabaseMetaData: 用来封装数据库连接对应数据库的信息,通过Connection提供的getMetaData()方法就可以获取数据库对应的DatabaseMetaData对象。

10,DatabaseMetaData详解

11,关于数据库中的schema和catalog

关于数据库中的schma和catalog

12,Mysql中的存储过程

数据库连接池:

1,DBCP数据源:Tomcat的连接池正是采用该连接池实现的。

**2,C3P0数据源:**相比DBCP,C3P0性能更胜一筹。C3P0不仅可以自动清理不再使用的Connection,还可以自动清理Statement和ResultSet。

第十四章:注解

1,注解能被用来为程序元素(类,方法,成员变量等)设置元数据。值得指出的是。注解不影响程序代码的执行,无论增加,删除注解,代码都始终如一地执行。如果希望让程序中的注解 在运行时起一定的作用,只有通过某种配套的工具对注解中的信息进行访问和处理,访问和处理注解的工具统称ART。

2,堆污染???

**3,**java用 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类

4,定义新的注解类型使用**@interface**关键字,在默认情况下,注解可用于修饰任何程序元素,包括类,接口,方法等。

5,注解还可以带成员变量,成员变量在注解定义中以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。 eg.

 public @interface MyTag{
​                         String   name();
​                         int age() default 32;
​                }
6,注解的分类

​ 1,标记注解:没有定义成员变量的注解类型被称为标记。仅利用自身的存在与否来提供信息。如前面介绍的@Override

​ 2,元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。

7,看到反射那一块在回过头来看P662页。

8,Java8新增的重复注解:

​ 使用@Repeatable修饰该注解,使用@Repeatable时必须为value成员变量指定值,该成员变量的值应该是一个”容器“注解----该”容器“注解可包含多个@FkTag,因此还需要定义如下的"容器"注解

9,Class类

10,关于重复注解

11,类型注解

12, APT:一种注解处理工具,它对源代码进行检测,并找出源文件所包含的注解信息,然后针对注解信息进行额外的处理。

第十五章 输入/输出

15.1 File类

1,File类是java.io包下代表与平台无关的文件和目录。不管是文件和目录都是使用File来操作的,File能新建,删除,重命名文件和目录,File不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。

2,File类可以文件路径字符串来创建File实例,既可以是相对路径,也可以是绝对路径。在默认情况下,系统总是依据用户的工作路径来解释相对路径,这个路径由系统属性“user.dir”指定,通常也就是运行Java虚拟机时所在的路径。???

3,**文件过滤器:**在File类的list()方法中可以接收一个FilenameFilter参数,通过该参数可以只列出符合条件的文件。

4,new File()

15.2 理解Java的IO流

1, Java的IO流是实现输入/输出的基础,它可以方便地实现数据的输出/输出操作,在Java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream),通过流的方式允许Java程序使用相同的方式来访问不同的输入/输出源。stream是从起源(source)到(sink)的有序数据。

2,数据从内存到硬盘,通常称为输出流,也就是说------这里的输入,输出都是从程序运行所在的内存的角度划分的。

3,Java的输入流主要由InputStream和Reader作为基类,而输出流主要由OutputStream和Writer作为基类,它们都是一些抽象基类,无法直接创建实例。

4,字节流和字符流的用法几乎完全一样,区别在于字节流和字符流操作的数据单元不同----字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符。

5,字节流主要由InputStream和OutputStream作为基类,而字符流主要由Reader和Writer作为基类。

6,还可分为节点流和处理流。从/向一个特定的IO设备(如磁盘,网络)读/写数据流,称为节点流,节点流也被称为低级流。处理流则用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。处理流也被称为高级流。

7,Java使用处理流来包装节点流是一种典型的装饰器设计摸式,处理流也被称为包装流

8,输入流使用隐式的记录指针来表示当前正在准备从哪个“水滴”开始读取,每当程序从中取出一个或多个“水滴”后,记录指针自动向后移动。InputStream和Reader里都提供一些方法来控制记录指针的移动。

9,处理流的功能主要体现在以下两个方面:

​ 1,性能的提高:主要以增加缓冲的方式来提高输入/输出的效率。

​ 2,操作的便捷:处理流可能提供了一系列便捷的方法来一次输入/输出大批量的内容,而不是输入/输出一个或多个“水滴”

**10,自动关闭资源的try语句**

11,输入流InputStream的reset()和mark()方法注意事项

12,因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。

13,关于读写数据时缓冲区的问题,flush

14, 使用处理流非常简单,只要流的构造器参数不是一个物理节点,而是已经存在的流,那么这种流就一定是处理流;而所有的节点流都是直接以物理IO节点作为构造器参数的。

15,PrintStream处理流来包装OutputStream。System.out的类型就是PrintStream

16,在使用处理流包装了底层节点流之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可

17,由于String是不可变的字符串对象,所以StringWriter使用StringBuffer作为输出节点。???

https://my.oschina.net/cmffire/blog/12106

18,推回输入流:PushbackInputStream和PushbackReader

19,转换流:InputStreamReader将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换成字符输出流

15.5 重定向标准输入输出:

1,Scanner.useDelimiter( )方法使用

15.6 Java虚拟机读写其他进程的数据:

1,使用Runtime对象的exec()方法可以运行平台上的其他程序,该方法产生一个Process对象,Process对象代表由Java程序启动的子进程。eg.Process p = Runtime.getRuntime().exec(“javac”)

-Dfile.encoding=GBK

2,exec()

3,子进程的错误流具体指的是什么?

15.7 RandomAccessFile

1, RandomAccessFile是Java 输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是,它支持“随机访问”的方式,程序可以跳转到文件的任意地方来读写数据。

2,RandomAccessFile有一个最大的局限,就是只能读写文件,不能读写其他IO节点。

3, RandomAccessFile依然不能向文件的指定位置插入内容,如果直接将文件内容记录指针移动到中间某位置后开始输出,则新输出的内容会覆盖文件中原有的内容。

15.8 对象序列化

1,对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上。

2,序列化机制允许将实现序列化的Java对象转换成字节序列。序列化机制使得对象可以脱离程序的运行而独立存在。

3,对象的序列化(Serialize)指将一个Java对象写入I/O流中,与此对应的是,对象的反序列化(Deserialize)则指从IO流中恢复该Java对象。

4,为了让某个类可序列化,该类必须实现如下两个接口之一

Serializable 实现该接口无须实现任何方法,它只是表明该类的实例是可序列化的。

Externalizable

5,所有可能在网络上传输的对象的类都应该是可序列化的,否则程序会出现异常;所有需要保存到磁盘里的对象的类都必须可序列化,比如Web应用中需要保存到HttpSession或ServletContext属性的Java对象。

6,当一个可序列化的类有多个父类时(包括直接父类和间接父类)。这些父类要么有无参数的构造器,要么也是可序列化的。如果父类是不可序列化的,只是带有无参数的构造器,则该父类中定义的成员变量不会序列化到二进制流中。

7,关于书上的P698页WriteObject程序中输出文本乱码的问

8,如果某个类的成员变量的类型不是基本类型或String类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型成员变量的类也是不可序列化的。

9,当使用Java序列化机制序列化可变对象时一定要注意,只有第一次调用wrtieObject()方法来输出对象时才会将对象转换成字节序列,在后面程序中即使该对象的实例变量发生了改变,再次调用writeObject()方法输出该对象时,改变后的实例变量也不会被输出。

10,**自定义序列化:**通过在实例变量前面使用transient关键字修饰,可以指定Java序列化时无须理会该实例变量。eg. private transient int age;

11, transient关键字只能用于修饰实例变量,不可修饰Java程序中的其他成分。

12,所有的单例类,枚举类在实现序列化时都应该提供readResolve()方法,这样才可以保证反序列化的对象依然正常。

13,Java还提供了另一种序列化机制,这种序列化的方式完全由程序员决定存储和恢复对象数据。要实现该目标,Java类必须实现Externalizable接口。

14,当使用Externalizable机制反序列化对象时,程序会先使用public的无参构造器来创建实例,所以实现Externalizable的序列化必须提供public的无参构造器。

15,transient实例变量也被称为瞬态实例变量

15.9 NIO

1, 当BufferedReader读取输入流中的数据时,如果没有读到有效数据,程序将在此处阻塞该线程的执行(使用InputStream的read()方法从流中读取数据时,如果数据源中没有数据,它也会阻塞该线程)。前面介绍的输入,输出流都是阻塞式的输入,输出。不仅如此,面向流的输入/输出系统一次只能处理一个字节,因此面向流的输入/输出系统通常效率不高。

2,从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新功能,这些功能统称为新IO(New IO,简称NIO),新增了很多用于处理输入输出的类,都放在java.nio包以及子包下。

3,新IO采用内存映射文件的方式来处理输入/输出的类,将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(这种方式模拟了操作系统上的虚拟内存的概念)。通过这种方式比传统的输入输出快的多。

4,Channel(通道)和**Buffer(缓冲)**是新IO中的两个核心对象。

5,Buffer中的三个重要的概念:容量(capacity),界限(limit),位置(position)

6,ByteBuffer还提供了一个allocateDirect()方法来创建直接Buffer。直接Buffer的创建成本比普通Buffer的创建成本高,但直接Buffer的读取效率更高。只有ByteBuffer才提供了allocateDirect()方法。

7,Channel类似于传统的流对象,但与传统的流对象有两个主要的区别:

​ 1> Channel可以直接将指定文件的部分或全部直接映射成Buffer。

​ 2> 程序不能直接访问Channel中的数据,包括读取,写入都不行,Channel只能与Buffer进行交互。

8,所有的Channel都不应该通过构造器来直接创建,而是通过传统的节点InputStream,OutputStream的getChannel()方法来返回对应的Channel,不同的节点流获得的Channel不一样。

9,Channel中最常用的三类方法是map(),read()和write()

10, 计算机里的文件,数据和图片文件只是一种表面现象,所有文件在底层都是二进制文件,即全部都是字节码。

11,Java提供了Charset来处理字节序列和字符序列(字符串)之间的转换关系,该类包含了用于创建解码器和编码器的方法,还提供了获取Charset所支持字符集的方法,Charest类是不可变的。

12,Java7新增了一个StandardCharsets类,该类里包含了UTF_8,UTF_16等类变量,这些类变量代表了最常用的字符集对应的Charset对象。

13,文件锁,文件锁在操作系统中是很平常的事情,如果多个运行的程序需要并发修改同一个文件时,程序之间需要某种机制来进行通信,使用文件锁可以有效阻止多个进程并发修改同一个文件,所以现在大部分操作系统都提供了文件锁的功能。

14,从JDK1.4的NIO开始,Java开始提供文件锁的支持,文件锁控制文件的全部或部分字节的访问。在NIO中,JAVA提供了FileLock来支持文件锁定功能,在FileChannel中提供的lock()/tryLock()方法可以获得文件锁FileLock对象,从而锁定文件。

15,P721页 在某些平台上,不能同步地锁定一个文件并把它映射到内存中。不太懂

15.10 Java7的NIO.2

1,Java7对原有的NIO进行了重大的改进,改进主要包括如下两方面的内容

​ 》提供了全面的文件IO和文件系统访问支持

​ 》基于异步Channel的IO

2,P722页,关于中文输出乱码,以及编码解码问题**???**

JAVA中的stream流

第十六章:多线程

1,单线程的程序只有一个顺序执行流,多线程的程序则可以包括多个顺序执行流,多个顺序流之间互不干扰。

2,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立的单位。

3,一般而言,进程包含如下三个特征:

​ 1,独立性

​ 2,动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。

​ 3,并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

并发性和并行性是两个概念,并行指在同一时刻,有多条指令在多个处理器上同时执行,并发指在同一时刻只能有一条指令执行,但多个进程指令被轮换执行,使得宏观上具有多个进程同时执行的效果。

4,现代的操作系统都支持多线程的并发,但在具体的实现细节上可能因为硬件和操作系统的不同而采用不同的策略。比较常用的方式有:共用式的多任务操作策略???;目前操作系统大多采用效率更高的抢占式多任务操作策略。???

5,多线程扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。线程也被称为轻量级进程,线程是进程的执行单元。就像进程在操作系统中的地位一样。当进程初始化后,主线程就被创建了。

6,线程是进程的组成部分,一个进程可以拥有多个多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,因为需要确保线程不会妨碍同一进程里的其他线程。

7,线程的执行是抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行。

8,一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少要包含一个线程。

9,归纳起来可以这样说,操作系统可以同时执行多个任务,每个任务就是进程,进程可以同时执行多个任务,每个任务就是线程。

10,线程在程序中是独立的,并发的执行流,与分隔的进程相比,进程中线程之间的隔离程度要小。它们共享内存,文件句柄和其他每个进程应有的状态。

11,线程共享的环境包括:进程代码段,进程的公有数据等。利用这些共享的数据,线程很容易实现相互之间的通信。

16.2 线程的创建和启动

1,Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。

2,通过继承Thread类来创建并启动多线程的步骤如下

​ 1,定义Thread类的子类,并重写该类的run()方法,该run方法的方法体代表了线程需要完成的任务,因此把run方法称为线程执行体。

​ 2,创建了Thread子类的实例,即创建了线程对象

​ 3,调用线程对象的start()方法来启动该线程。

3,Thread.currentThread().getName()获取当前线程

4,使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

5,实现Runnable接口来创建并启动多线程的步骤如下

​ 1,定义Runnable接口的实现类,并重写该方法的run()方法,该run()方法的方法体同样是该线程的线程执行体

​ 2,创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

3,调用线程对象的start()方法来启动该线程

6,Runnable接口和Callable接口都是函数式接口

7,使用Callable和Future创建线程

​ 1,从Java5开始,Java提供了Callable接口,该接口怎么看都像是Runnable接口的增强版,Callable接口提供了一个call()方法可以作为线程执行体,但call方法比run方法功能更加强大。

​ 1,call()方法可以有返回值

​ 2,call()方法可以声明抛出异常

​ 2,Callable接口不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target.

​ 3, JAVA5提供了Future接口来实现Callable接口里的call()方法返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现了Future接口,并且实现了Runnable接口-----可以作为Thread类的target。

​ 4,Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同。

​ 5,Future接口中的get方法:future.get(5000, TimeUnit.MILLISECONDS)

16.3 线程的生命周期

1,当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New),就绪(Runnable),运行(Running),阻塞(Blocked)和死亡(Dead)5种状态。

2,如果直接调用线程对象的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前其他线程无法并发执行。也就是说,如果直接调用的话,系统会把线程对象当成一个普通对象。

3,如果希望调用子线程的start()方法后子线程立即开始执行,程序可以使用Thread sleep(1)来让当前运行的线程(主线程)睡眠一毫秒—1毫秒就够了。

4,当一个线程开始运行后,它不可能一直处于运行状态,线程在运行过程指中需要被中断,目的是使其他线程获得执行的机会,线程调度的细节取决于底层平台所采用的策略。

5,当前正在执行的线程被阻塞之后,其他线程就可以获得执行的机会。被阻塞的线程会在合适的时候重新进入就绪状态,注意是就绪状态而不是运行状态。也就是说,被阻塞的线程的阻塞解除后,必须重新等待线程调度再次调度它。

6,线程让步(yield方法)

由运行态到就绪态,停止一下后再由就绪态到运行态

  • 暂停当前正在执行的线程对象,并执行其他线程。
  • 意思就是调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。
  • 但是yield不能立刻交出CPU,会出现同一个线程一直执行的情况,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
  • 注意调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的

7,线程死亡

线程会以以下三种方式结束,结束后就处于死亡状态了。

​ 1,run()或者call()方法执行完成,线程正常结束

​ 2,线程抛出一个未捕获的Exception或Error

​ 3, 直接调用该线程的stop()方法来结束该线程----该方法容易导致死锁,通常不推荐使用。

8为了测试某个线程是否死亡,可以调用线程对象的isAlive()方法,当线程处于就绪,运行,阻塞三种状态时,该方法返回true;处于新建,死亡状态时返回false.

16.4 控制线程
1,join线程:让一个线程等待另一个线程完成的方法----join()方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。

2,后台线程:有一种线程,它是在后台运行的,它的任务是为其他的线程提供服务,这种线程被称为“后台线程”,又被称为“守护线程”或“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。

3,后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。isDaemon()方法用于判断指定线程是否为后台线程

4,线程睡眠*:sleep(). 线程进入阻塞状态,在其睡眠时间内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程。Thread还提供了一个与sleep()方法有点相似的yield()静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。只是让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能当莫格线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。

5,当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行的机会。

16.5 线程同步

1,线程安全问题

2, 为了解决这个问题,Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块。语法格式如下:

​ synchronized(obj){

​ 此处的代码就是同步代码块

​ }

3,“加锁—修改—释放锁”的逻辑,任何线程在修改指定资源之前,首先对该资源加锁,在加锁期间其他线程无法修改该资源,当该线程修改完成后,该线程释放队该资源的锁定。通过这种方式就可以保证并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(也被称为临界区。)

4,同步方法:与同步代码块对应,Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法。

5,对于synchronized修饰的实例方法(非static方法)而言,无须显式指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象。

6,可变类的线程安全是以降低程序的运行效率作为代价的。

7,JDK所提供的StringBuilder, StringBuffer就是为了照顾单线程环境和多线程环境所提供的类,在单线程环境下应该使用StringBuilder来保证较好的性能;当需要保证多线程安全时,就应该使用StringBuffer。

8,释放同步监视器的锁定,程序无法显式释放对同步监视器的锁定。

9,同步锁(Lock):从Java5开始,java提供了一种功能更强大的线程同步机制----通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。

10,在实现线程安全的控制中,比较常用的是ReentrantLock(可重入锁),使用该Lock对象可以显式的加锁,释放锁。

11,死锁:当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有措施来处理死锁的情况,所以多线程编程时应该采取措施避免死锁出现。

12,由于Thread类的suspend()方法也很容易导致死锁,所以Java不再推荐使用该方法来暂停线程的执行。

16.6 线程通信

1,当线程在系统内运行时,线程的调度具有一定的透明性,程序通常无法准确控制线程的轮换执行,但Java也提供了一些机制来保证线程协调运行。

2,使用Condition控制线程通信

​ 2.1,如果程序不使用synchronized()关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能使用wait(),notify(),notifyAll()方法进行线程通信了。

3,当使用Lock对象来保证同步时,Java提供了一个Condition类来保持协调。使用Condition可以让那些已经得到Lock对象却无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他处于等待的线程。

4,使用阻塞队列(BlockingQueue)控制线程通信。程序的两个线程通过交替向BlockingQueue中放入元素,取出元素,即可很好的控制线程的通信。

16.7 线程组和未处理的异常

1,Java使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

2,一旦某个线程加入了指定线程组后,该线程将一直属于该线程组,直到该线程死亡,线程运行中不能改变它所属的线程组。

3,线程中的异常处理器与通过catch捕获异常是不同的----当使用catch捕获异常时,异常不会向上传播给上一级的调用者;但使用异常处理器对异常进行处理后,异常依然会传播给上一级的调用者。

16.8 线程池

1,系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互。在这种情况下。使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期限很短暂的线程时,更应该考虑使用线程池。

2,使用线程池可以有效地控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能剧烈下降,甚至导致JVM崩溃,而线程池的最大线程参数可以控制系统中的并发线程数不超过此数。

3,ExecutorServce newWorkStealingPool

4,用完一个线程池后,应该调用该线程池的shutdown()方法,该方法将启动线程池的关闭序列,调用shutdown()方法后的线程池不再接收新任务,但会将以前所有已提交的任务执行完成。

5,另外也可调用线程池的shutdown()方法来关闭线程池,该方法试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

6, Runnable和Callable都是函数式接口。

7,Java8增强的ForkJoinPool

​ 7.1 ForkJoinPool支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。其是ExecutorService的实现类,因此是一种特殊的线程池。

​ 7.2 FrokJoinTask代表一个可以并行,合并的任务。ForkJoinTask是一个抽象类,它还有两个抽象类:RecursiveAction(代表没有返回值的任务)和RecursiveTask(代表有返回值的任务)

​ 7.3 工作窃取算法 work-stealing

​ 7.4 java 并发之awaitTermination的使用

​ 7.5 ForkJoinPool commonPool(): 该方法返回一个通用池,通用池的运行状态不会受shutdown()或shutdownNow()方法的影响。

16.9 线程相关类
1,ThreadLocal类

​ 1.1 线性局部变量(ThreadLocal)就是为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量一样。

​ 1.2 ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式;而ThreadLocal是为了隔离多个线程的数据共享,从根本上避免多个线程之间对共享资源(变量的竞争),也就不需要对多个线程进行同步了。

2,包装线程不安全的集合

2.1 ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap等都是线程不安全的。如果程序中有多个线程可能访问以上这些集合,就可以使用Collections提供的类方法把这些集合包装成线程安全的集合。

第17章 网络编程

1,所谓计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大,功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件,软件,数据信息等资源。

2,网络通常是按照规模大小和延伸范围来分类的,常见的划分为:局域网(LAN),城域网(MAN),广域网(WAN)。Internet可以视为世界上最大的广域网。

3,通信协议通常由三部分组成:一是语义部分,用于决定双方对话的类型;二是语法部分,用于决定双方对话的格式;三是变换规则,用于决定通信双方的应答关系。

4,国际标准组织ISO于1978年提出“开放系统互连参考模型”,即著名的OSI(Open System Interconnection)

5, OSI把计算机网络分成物理层,数据链路层,网络层,传输层,会话层,表示层,应用层七层。

17.2 Java的基本网络支持

1,Java为网络支持提供了java.net包,该包下的URL和URLConnection等类提供了以编程方式访问Web服务的功能。

2,Java提供了InetAddress类来代表IP地址,其下还有两个子类:Inet4Address,Inet6Address

3, URL对象代表统一资源定位器,可以由协议名,主机,端口和资源组成。

4,URL类提供了多个构造器用于创建URL对象,一旦获得了URL对象之后,就可以调用方法来访问该URL对应的资源。

5,关于URLConnection

6, 如果既要使用输入流读取URLConnection响应的内容,又要使用输出流发送请求参数,则一定要先使用输出流,再使用输入流。

7,URLConnection中的getInputStream和getOutputStream

8,HTTP请求头和响应头详解

17.3 基于TCP协议的网络编程

1,TCP/IP协议是一种可靠的网络协议,它在通信两端各建立一个Socket,从而在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。

2,Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

3,Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。

4,Socketd对象提供了一个setSoTimeout(int timeout)方法来设置超时时长。

对相同资源的并发访问,是多个线程之间进行通信的有效方式;而ThreadLocal是为了隔离多个线程的数据共享,从根本上避免多个线程之间对共享资源(变量的竞争),也就不需要对多个线程进行同步了。

2,包装线程不安全的集合

2.1 ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap等都是线程不安全的。如果程序中有多个线程可能访问以上这些集合,就可以使用Collections提供的类方法把这些集合包装成线程安全的集合。

第17章 网络编程

1,所谓计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大,功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件,软件,数据信息等资源。

2,网络通常是按照规模大小和延伸范围来分类的,常见的划分为:局域网(LAN),城域网(MAN),广域网(WAN)。Internet可以视为世界上最大的广域网。

3,通信协议通常由三部分组成:一是语义部分,用于决定双方对话的类型;二是语法部分,用于决定双方对话的格式;三是变换规则,用于决定通信双方的应答关系。

4,国际标准组织ISO于1978年提出“开放系统互连参考模型”,即著名的OSI(Open System Interconnection)

5, OSI把计算机网络分成物理层,数据链路层,网络层,传输层,会话层,表示层,应用层七层。

17.2 Java的基本网络支持

1,Java为网络支持提供了java.net包,该包下的URL和URLConnection等类提供了以编程方式访问Web服务的功能。

2,Java提供了InetAddress类来代表IP地址,其下还有两个子类:Inet4Address,Inet6Address

3, URL对象代表统一资源定位器,可以由协议名,主机,端口和资源组成。

4,URL类提供了多个构造器用于创建URL对象,一旦获得了URL对象之后,就可以调用方法来访问该URL对应的资源。

5,关于URLConnection

6, 如果既要使用输入流读取URLConnection响应的内容,又要使用输出流发送请求参数,则一定要先使用输出流,再使用输入流。

7,URLConnection中的getInputStream和getOutputStream

8,HTTP请求头和响应头详解

17.3 基于TCP协议的网络编程

1,TCP/IP协议是一种可靠的网络协议,它在通信两端各建立一个Socket,从而在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。

2,Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

3,Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。

4,Socketd对象提供了一个setSoTimeout(int timeout)方法来设置超时时长。

5, NIO为什么SelectionKey在被轮询后需要remove()
6,使用Java7的AIO实现非阻塞通信。Java7的NIO.2提供了异步Channel支持,这种异步Channel可以提供更高效的IO,这种基于异步Channel的IO机制也被称为异步IO。

17.4 基于UDP协议的网络编程
  • UDP协议是一种不可靠的协议,它在通信实例的两端各建立一个Socket,但这两个Socket之间并没有虚拟链路,这两个Socket只是发送,接收数据报的对象。Java提供了DatagramSocket对象作为基于UDP协议的Socket。
  • UDP协议的主要作用是完成网络数据流和数据报之间的转换-----在信息的发送端,UDP协议将网络数据流封装成数据报,然后将数据报发送出去;在信息的接收端,UDP协议将数据报转换成实际内容。
  1. UDP协议和TCP协议简单对比如下:
  • TCP协议:可靠,传输大小无限制,但是需要连接建立时间,差错控制开销大
  • UDP协议:不可靠,差错控制开销较小,传输大小限制在64KB以下,不需要建立连接。

4,Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的。
5,使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪里,而是由DatagramPacket自身决定数据报的目的地。
6,SocketAddress对象实际上就是一个IP地址和一个端口号,也就是说,SocketAddress对象封装了一个InetAddress对象和一个代表端口的整数。
7,使用MulticastSocket实现多点广播,DatagramSocket只允许数据报发送给指定的目标地址,而MulticastSocket可以将数据报以广播方式发送到多个客户端。
8,MulticastSocket是DatagramSocket的一个子类。

17.5 使用代理服务器

1,代理服务器主要提供如下两个功能:

  • 突破自身IP的限制,对外隐藏自身IP地址。突破IP限制包括访问国外受限站点,访问国内特定单位,团队的内部资源。
  • 提高访问速度,代理服务器提供的缓冲功能可以避免每个用户都直接访问远程主机,从而提高客户端访问速度。

第18章 类加载机制与反射

18.1类的加载,连接和初始化

1,当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这三个步骤,所以有时这三个步骤统称为类加载或类初始化。
2,类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之创建一个java.lang.Class对象。
3,当类被连接之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可分为如下三个阶段:

  1. 验证
  2. 准备
  3. 解析

4,java中的直接引用和符号引用
5,类的初始化,在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。在Java类中对类变量指定初始值有两种方式:1,声明类变量时指定初始值;2,使用静态初始化块为类变量指定初始值。
6,对于一个final型的类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于“宏变量”。java会在编译时直接把这个类变量出现的地方替换成它的值,因此即使程序中使用该静态变量,也不会导致该类的初始化。

18.2 类加载器

1,类加载器负责将.class文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之生成对应的java.lang.Class对象。
2,JVM的类加载机制主要有如下三种:

  • 全盘负责
  • 父类委托
  • 缓存机制

3,URLClassLoader类,Java为ClassLoader提供了一个URLClassLoader类,该类也是系统类加载器和扩展类加载器的父类(此处的父类,就是指类与类之间的继承关系)。
4,创建URLClassLoader时传入一个URL数组参数,该ClassLoader就可以从指定的资源中加载指定类,URL可以以file:为前缀,表明从本地文件系统加载;可以以http:为前缀,表明从互联网通过HTTP访问来加载;也可以以ftp:为前缀,表明从互联网通过FTP访问来加载。

18.3 通过反射查看类信息

1,编译时根本无法预知该对象和类可能属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。
2,在Java程序中获得Class对象通常有如下三种方式

  • 使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)
  • 调用某个类的class属性来获取该类对应的Class对象
  • 调用某个对象的getClass()方法

3,对于只能在源代码上保留的注解,使用运行时获得的Class对象无法访问到该注解。
4,一些常用注解
5,使用javac命令编译Java源文件时,默认生成的class文件并不包含方法的形参名信息。

18.4 使用反射生成并操作对象

1,在很多JavaEE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序需要根据该字符串来创建对应的实例,就必须使用反射。
2,每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它对应的方法。在Method里包含一个**invoke()**方法,该方法的签名如下。
Object invoke(Object obj,Object… args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。

18.5 使用反射生成JDK动态代理

1,JDK动态代理只能为接口创建动态代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值