java基本程序设计环境
-
java数据类型:强类型,每个变量必需声明一种类型
-
整型:int(4字节) short(2字节) long(8字节) byte(2字节),整型范围与平台无关,java没有unsigned整型类型
-
浮点类型:float(4字节) double(8字节),三个特殊浮点数值:正无穷大(Double.POSITIVE_INFINITY),负无穷(Double.BEGATIVE_INFINITY),NAN(Double.NAN);浮点数的误差:二进制无法精确表示某些分数(如1/10)
-
char类型:使用Unicode字符集,范围:\u0000-\uffff
-
boolean类型
-
-
java变量
-
变量声明:每个变量有一个类型,位于变量名之前
-
变量名:以字母开头,由字母和数字组成(字母包括 ’ A ’ ~ ’ Z ’ 、 ’ a 1 ~ ’ z 、或在某种语言中表示字母的任何 Unicode 字符),变量名大小写敏感,不可使用java保留字作为变量名
-
变量初始化:使用赋值语句显示初始化,不可使用未初始化的变量(变量的声明尽可能地靠近变量第一次使用的地方)
-
常量:关键字final指示常量,表示该变量只能赋值一次,习惯上常量使用全大写字母表示;staic final指示类常量,可在一个类的多个方法调用
-
-
-
运算
-
整数被0除会产生异常,浮点数被0除会得到无穷大或NAN;
-
strictfp关键字:使用严格的浮点计算,每次计算的中间值存储在 80 位的寄存器中 , 最终结果截断为 64 位
-
数值类型转换:运算时将操作数转换为相同数据类型(向精度高的转换)
-
强制类型转换:可能导致数据丢失
double x = 9.997; int nx = (int)x;//double类型强制转换为int类型
-
其他运算:赋值运算(+= -= *=...)自增自减(++ --)关系逻辑(&& || > ==...),逻辑运算具有短路运算特点,位运算(^ & | ~ >> << <<<)
-
-
字符串
-
java字符串是Unicode字符序列,java没有内置的字符串类型,标准类库中有String类
-
子串:String类的substring方法可从长字符串中提取字串
-
拼接:可使用+连接两个字符串,将多个字符串连接可使用String.join方法
-
String对象不可修改(即不可变字符串),字符串变量的修改实质上是引用另一个字符串。不可变字符串的优点:实现字符串的共享
-
检测字符串相等:equals方法,==比较的是字符串是否是同一个对象,而非字符串内容是否相等
-
空串与null:空串""是长度为0的字符串,null是未指向对象的字符串变量
-
构建字符串:StringBuilder类
-
-
输入输出
-
标准输出流:System.out;标准输入流:System.in,使用标准输入流需要构造Scannner对象,并与System.in关联
Scanner scan = new Scanner(System.in);//使用标准输入流
-
格式化输出:System.out.printf,每一个以 % 字符开始的格式说明符都用相应的参数替换 ;使用静态的 String.format 方法创建一个格式化的字符串, 不打印输出
-
文件输入与输出
-
文件读取:使用File对象构造Scanner对象(如果使用文件名字符串作为参数传入,会被Scanner对象解释为数据)
Scanner in = new Scanner(Paths.get("niyflle.txt"),"UTF-8");
-
文件写入:构造PrintWriter对象,只须提供文件名(如果文件不存在,则自动新建文件)
PrintWriter out = new PrintlulriterC ' myfile txt" , "UTF - 8" ) ;
-
-
-
控制流程
-
块作用域
-
if else语句
-
while循环
-
do while循环
-
for循环(在循环中判断浮点数相等可能会出现死循环)
-
switch语句
-
对象与类
-
面向对象程序设计概述
-
类:构造对象的模板
-
对象:三特性(行为、状态、标识)
-
类的设计:名词——》成员域 动词——》方法
-
类之间的关系:依赖(use-a:一个类的方法操纵另一个类的对象)、聚合(has-a:一个类的对象包含另一个类的对象)、继承(is-a)
-
-
用户自定义类
-
数据的封装:私有成员域+setter方法+getter方法(不要编写返回引用对象的setter方法,因为返回的是引用值,会导致私有成员域在外部被修改,破坏封装性;如果需要返回对象的引用,应该先对其进行克隆,将其保存为另一个对象副本)
-
-
静态域与静态方法
-
静态域(类域):每个类中只有一个;实例域:每个对象都有一个拷贝
-
静态常量:static final
-
静态方法:不能向对象实施操作的方法,不可访问实例域,通过类名调用(也可通过对象调用)。
-
-
方法参数
-
java参数传递:值传递
-
基本数据类型:不可被修改
-
引用数据类型(值为地址):可被修改
-
一个方法不能修改一个基本数据类型的参数 (即数值型或布尔型 ) 。
-
一个方法可以改变一个对象参数的状态 。
-
一个方法不能让对象参数引用一个新的对象 。
-
-
-
对象构造
-
重载:多个相同名字但参数不同的方法
-
默认域初始化:
-
如果在构造器中没有显式地给域赋予初值 , 那么就会被自动地赋为默认值 : 数值为 0 、布尔值为 false 、 对象引用为 null 。
-
无参构造器:只有类没有编写构造器时,系统才会提供一个无参构造器
-
构造器中调用另一个构造器:this
-
初始化块:先于构造器被初始化
-
-
包
-
使用包可以确保类名唯一
-
一个类可以使用所属包内的所有类以及其他包的公共类
-
使用import导入类和静态方法和静态域
-
要将一个类放人包中 , 就必须将包的名字放在源文件的开头
-
没有指定 public 或 private 的部分 ( 类 、 方法或变量 )可以被同一个包中的所有方法访问 。
-
-
类路径
-
类的路径必须与包名匹配 。
-
为了使类能够被多个程序共享, 需要做到下面几点 :
-
把类放到一个目录中, 例如 / home / user / classdir 。 需要注意, 这个目录是包树状结构的基目录 。 如果希望将 com . horstmann . corejava . Employee 类添加到其中, 这个 Employee . class类文件就必须位于子目录 / home / user / classdir / com / horstmann/corejava 中 。
-
将 JAR 文件放在一个目录中 , 例如 : / home / user / archives 。
-
设置类路径 ( classpath ) 。 类路径是所有包含类文件的路径的集合 。
-
-
-
文档注释
-
在源代码中添加以专用的定界符 /** 开始的注释,可以使用javadoc生成注释文档
-
需要编写注释的部分(注释:
-
包
-
公有类与接口
-
公有的和受保护的构造器及方法
-
公有的和受保护的域
-
-
类注释:放在import语句之后,类定义之前
-
方法注释:放在方法之前,可用以下标记
-
@para变量描述
-
@return描述
-
@throws描述
-
-
域注释:只需要对公有域注释
-
通用注释:
-
@author姓名
-
@version:产生版本描述
-
@since:引入特性的版本描述
-
-
-
类设计技巧
-
保证数据私有
-
要对数据初始化
-
不要在类中使用过多基本数据类型
-
不是所有域都需要getter和setter
-
将方法过多的类进行分解
-
类名和方法名要体现功能
-
优先使用不可变的类
-
继承
-
类、超类和子类
-
定义子类:使用关键字extends从一个类继承(java中所有继承都是公有继承)
-
extends表示正在构造的新类派生于已存在的类,已存在的类称为超类、基类或父类,新类称为子类、派生类
-
子类自动继承超类的成员域和方法,超类不可使用子类的域和方法,子类可使用超类的域和方法
-
-
覆盖方法(重写)
-
对于超类不适用的方法,子类可以提供一个新方法来覆盖超类中的这个方法
-
子类的方法不能直接访问超类的私有域(虽然每个子类对象都拥有这些成员域),访问私有域需要通过公有的接口
-
调用超类的方法,需要使用关键字super
-
-
子类构造器:
-
因为子类不能直接访问超类的私有域,所以可以调用超类的构造器来对该部分进行初始化,调用方法:super()
-
如果子类的构造器没有显式地调用超类的构造器 , 则将自动地调用超类默认 ( 没有参数 )的构造器 。 如果超类没有不带参数的构造器, 并且在子类的构造器中又没有显式地调用超类的其他构造器 则 Java 编译器将报告错误 。
-
子类类型的变量既可以引用子类对象也可引用超类对象(多态)
-
-
多态
-
对象变量是多态的,子类类型的变量可以引用子类对象也可以引用超类对象
-
不能把超类类型的引用赋给子类变量
-
-
阻止继承:final类和方法
-
不能被扩展的类称为final类,使用final修饰符声明
-
被声明为final的方法不能被覆盖
-
final类中的所有方法自动成为final方法
-
-
强制类型转换
-
将一个子类的引用赋给一个超类变量 , 编译器是允许的 。 但将一个超类的引用赋给一个子类变量, 必须进行类型转换。
-
只能在继承层次内进行类型转换 。
-
在将超类转换成子类之前 , 应该使用 instanceof 进行检查 。
-
-
抽象类
-
使用abstract关键字声明的方法是抽象的,不需要实现
-
包含一个或多个抽象方法的类必须被声明为抽象类
-
抽象类也可以有具体的数据域和方法
-
抽象方法的具体实现在子类中
-
如果扩展抽象类没有实现所有抽象类方法,那么子类也必须定义为抽象的
-
不含抽象方法的类也可以被定义为抽象类
-
抽象类不能被实例化,可以定义抽象类的变量,但只能引用非抽象类型的子类实例
-
-
受保护访问
-
private私有欲对其他类是不可见的,子类也不能访问超类的私有域
-
如果子类希望访问超类的某些域或方法,可以将域声明为protected
-
-
-
Object:所有类的超类
-
可以使用Object类型的变量引用任何类型的对象
-
equals方法:检测两个对象是否相等,即两个对象是否具有相同的引用
-
相等测试与继承,equals方法的要求
-
自反性:对任意非空引用x,x.equals(x)返回true
-
对称性:对任意非空引用x、y,当且仅当 y.equals(x)返回true, x.equals(y)也应该返回true。
-
传递性:对于任何引用x、y和z, 如果x.equals(y)返回true, y.equals(z)返回true,x.equals(z)也应该返回true。
-
一致性: 如果x和y引用的对象没有发生变化, 反复调用x.equals(y) 应该返回同样的结果。
-
对于任意非空引用 x, x.equals(null) 应该返回 false
-
-
hashCode方法:获得对象的散列值
-
toString方法:返回表示对象值的字符串,格式(类名[域值])
-
-
泛型数组列表
-
java允许运行时确定数组的大小
-
ArrayList泛型类:可自动调节数组大小(数组列表元素操作:add()添加,set()修改, get()获取,remove()删除)
-
-
对象包装器与自动装箱
-
java所有基本类型都有一个与之对应的类,称为包装器
-
对象包装器是不可变的,即一旦构造了包装器就不允许更改包装在其中的值;包装器类是final的,不能定义他们的子类
-
泛型中的类型参数不允许是基本数据类型,需要用到基本类型的包装器
-
基本类型自动转换为包装器类型的特性称为自动装箱
-
将包装器类型赋值给基本类型时,会自动拆箱
-
包装器类引用可以为nll,可能产生NullPointerException
-
-
参数数量可变的方法
-
java提供了可以用可变的参数数量调用的方法,如printf()方法
System.out.printf("%d",n);
System.out.printf("%d,%s",n,"weights")
-
实际上,java通过使用参数数组作为参数来实现可变参数数量的方法,格式Object...values
-
-
枚举类
-
枚举类型是一个实例数量确定的类
-
所有枚举类型继承自Enum类,toSring()方法可以返回枚举常量名,静态values()方法可以返回一个包含全部枚举常量的数组
-
-
反射
-
什么是反射?反射机制是能够分析类能力的程序
-
反射机制能干什么?
-
在运行时分析类的能力
-
在运行时查看对象
-
实现通用的数组操作代码
-
利用Method对象
-
-
Class类
-
程序运行时,系统会为所有对象维护一个被称为运行时的类型标识,该信息跟踪这每个对象所属的类
-
Class类保存着这些信息,可以通过其访问这些信息。获得Class对象的三种方法
-
Object类中的getClass()方法会返回一个Class类型实例,Class类的getName()方法会返回类的名字(如果类在一个包里,包的名字也作为类名的一部分),
-
还可以通过静态方法forName()获得类名对应的Class对象
-
如果 T 是任意的 Java 类型 ( 或 void 关键字) ,T.class 将代表匹配的类对象 。
-
-
String s;
Class c1 = s.getClass; //返回一个Class类型实例
System.out.print(c.getName()); //输出s的类名String
Class c2 = Class.forName("java.util.Random");//获得java.util.Random对应的Class对象
Class c3 = Random.class;
-
class对象实际上表示的是一个类型,这个类型可能不是一种类
-
虚拟机为每种类型管理一个Class对象
-
-
利用反射分析类的能力:java.lang.reflect中的类
-
Field:描述类的域:getName()返回项目名;getType()返回描述域所属类型的 Class 对象;
-
Method:描述类的方法
-
Constructor:描述类的构造器
-
Modifier:描述类的域、方法、构造器的修饰符
-
Class的getFields、getMethods、getConstructors方法分别返回类提供的public域、方法和构造器数组,包括超类的公有成员;
-
Class 类的 getDeclareFields 、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域 、 方法和构造器, 其中包括私有和受保护成员, 但不包括超类的成员 。
-
-
在运行时使用反射分析对象
-
利用反射机制可以查看在编译时还不清楚的对象域
-
Field的get(obj)返回一个对象,其值为obj域的当前值,set(obi,value)修改修改obj对象域的值
-
反射机制的默认行为受限于java的访问控制,如果一个 Java 程序没有受到安全管理器的控制 , 就可以调用 Field 、 Method 或Constructor 对象的 setAccessible 方法
覆盖访问控制 。
-
-
使用反射编写泛型数组代码
-
调用任意方法
-
Method类的invoke方法
-
-
继承的设计技巧
-
将公共操作和域放在超类
-
不要使用受保护的域
-
使用继承实现"is-a"
-
除非所有继承的方法都有意义, 否则不要使用继承
-
在覆盖方法时, 不要改变预期的行为
-
使用多态 , 而非类型信息
-
不要过多地使用反射
-
接口、lambda表达式与内部类
-
接口
-
接口概念
-
接口不是类,是对类的一组需求描述
-
接口中的所有方法自动属于public
-
为了让类实现一个接口, 通常需要下面两个步骤 :1 ) 将类声明为实现给定的接口 。2 ) 对接口中的所有方法进行定义 。
-
要将类声明为实现某个接口 , 需要使用关键字 implements
-
-
接口特性
-
接口不是类,不能使用new运算符实例化一个接口
-
可以声明接口的变量,接口变量必须引用实现了接口的类对象
-
可以使用instancof检查一个对象是否实现了接口
-
接口可以被扩展
-
接口不能包含实例域或静态方法,但可以包含常量,即其中的域被自动设为public static final
-
每个类只能有一个超类,但可以实现多个接口
-
-
静态方法
-
Java SE 8允许在接口中添加静态方法,虽然合法但违背了接口作为抽象规范的初衷
-
目前通常的做法都是将静态方法放在伴随类中
-
-
默认方法
-
可以为接口提供默认实现,使用default修饰符标记
-
对于只关心接口中的部分方法时,可以将接口所有方法声明为默认方法,只实现自己需要的接口方法。
-
-
解决默认方法冲突
-
如果先在一个接口中将一个方法定义为默认方法 , 然后又在超类或另一个接口中定义了同样的方法,那么
-
超类优先
-
接口冲突:必需覆盖冲突的方法
-
-
-
接口示例:接口与回调,Comparator接口,对象克隆
-
-
lambda表达式
-
为什么引入lambda表达式
-
lambda表达式是一个可传递的代码块
-
需要将一个代码块传递给某个对象,该代码块在将来某段时间被调用
-
java不能直接传递代码段,必须构造一个对象 , 这个对象的类需要有一个方法能包含所需的代码
-
-
lambda表达式的语法
-
lambda表达式是一个代码块,以及必须传入代码的变量规范
-
lambda表达式的一种形式:(参数)->表达式(如果如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中,并包含显式的return 语句 。
-
即使lambda表达式没有参数也要提供空括号
-
如果可以推导出一个 lambda 表达式的参数类型, 则可以忽略其类型
Comparator<String> comp
= ( first , second ) // Same as ( String first , String second )
-> first.length() - second.length() ;
-
如果方法只有一个参数 , 而且这个参数的类型可以推导得出 , 那么甚至还可以省略小括号
ActionListener listener = event - >
System.out.println ( " The time is " + new Date() " ) ;
// Instead of (event) - > . . or ( ActionEvent event) ->...
-
无需指定 lambda 表达式的返回类型 。 lambda 表达式的返回类型总是会由上下文推导得出
-
-
函数式接口
-
对于只有一个抽象方法的接口, 需要这种接口的对象时 , 就可以提供一个 lambda 表达式。这种接口称为函数式接口(functional interface)。
-
方法引用:方法引用等价与lambda表达式,使用::分割方法名和类名或对象名
Timer t=new Timer (1000 , System.out::println );//将println方法传递给构造器,System,out.println等价于event->System.out.println(event)
-
可以在方法引用中使用this和super
-
-
构造器引用:方法名为new
-
变量作用域:
-
lambda 表达式可以捕获外围作用域中变量的值,要确保所捕获的值是明确定义的
-
在 lambda 表达式中, 只能引用值不会改变的变量
-
如果在 lambda 表达式中引用变量, 而这个变量可能在外部改变 , 这也是不合法的
-
lambda 表达式中捕获的变量必须实际上是最终变量 ( effectivelyfinal 。实际上的最终变量是指, 这个变量初始化之后就不会再为它赋新值 。
-
lambda 表达式的体与嵌套块有相同的作用域,在 lambda 表达式中声明与一个局部变量同名的参数或局部变量是不合法的。
-
在一个 lambda 表达式中使用 this 关键字时, 是指创建这个 lambda 表达式的方法的 this参数 。
-
-
处理lambda表达式
-
使用lambda表达式的重点是延迟执行,如以下情况:
-
在一个单独的线程中运行代码 ;
-
多次运行代码 ;
-
在算法的适当位置运行代码 ( 例如 , 排序中的比较操作 ;)
-
发生某种情况时执行代码 ( 如, 点击了一个按钮 , 数据到达, 等等 ;
-
只在必要时才运行代码 。
-
-
要接受 lambda 表达式 , 需要选择 (偶尔可能需要提供) 一个函数式接口
-
-
-
内部类
-
内部类是定义在另一个类中的类,使用内部类的原因有:
-
内部类方法可以访问该类定义所在作用域的数据,包括私有数据
-
内部类可以对同一个包中的其他类隐藏起来
-
使用匿名内部类可以更便捷地定义一个回调函数
-
-
使用内部类访问对象状态
-
内部类既可以访问自身的数据域, 也可以访问创建它的外围类对象的数据域 .
-
内部类的对象总有一个隐式引用, 它指向了创建它的外部类对象 。
-
只有内部类可以定义为私有
-
-
局部内部类
-
局部类不能用 public 或 private 访问说明符进行声明 。 它的作用域被限定在声明这个局部类的块中 。
-
局部类对外部世界可以完全地隐藏起来
-
-
匿名内部类
-
只创建一个类的对象,不需要命名
-
匿名内部类没有类名,不能有构造器,将构造器参数传给超类构造器
-
-
静态内部类:只是为了把一个类隐藏在另外一个类的内部 , 并不需要内部类引用外围类对象 。
-
-
代理
-
代理的功能:在运行时创建实现给定接口的新类
-
使用时机:需要构造在编译时无法知道确切类型的表示接口的Class对象
-
代理类能在运行时创建实现指定接口的全新的类,具有指定接口的全部方法以及Object类的全部方法
-
创建代理类对象:使用Proxy类的newProxyInstance方法
-
异常、断言和日志
-
处理错误
-
如果由于出现错误而使得某些操作没有完成, 程序应该 :
-
返回到一种安全状态, 并能够让用户执行一些其他的命令 ; 或者
-
允许用户保存所有操作的结果, 并以妥善的方式终止程序
-
-
异常分类
-
异常对象都是派生与Throwable的一个实例
-
异常分为Error和Exception
-
Error:Java 运行时系统的内部错误和资源耗尽错误,不能抛出
-
Exception:分为RuntimeException和其他异常。由程序错误导致的异常属于 RuntimeException;而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常
-
派 生 于 Error 类 或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常 , 所有其他的异常称为受查 ( checked ) 异常
-
-
-
声明受查异常
-
方法应该在其首部声明所有可能抛出的异常
-
需要抛出异常的情况:
-
调用一个抛出受检异常的方法
-
程序运行过程中发现错误
-
程序出现错误
-
java虚拟机和运行时库出现的内部错误
-
-
如果一个方法有可能抛出多个受查异常类型 , 那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开 。
-
子类方法中声明的受查异常范围必须低于超类,如果超类方法没有抛出任何受查异常, 子类也不能抛出任何受查异常 。
-
-
如何抛出异常
-
抛出异常的操作流程
-
找到一个合适的异常类
-
创建这个类的一个对象
-
将对象抛出
-
-
一旦方法抛出了异常,这个方法就不能返回到调用者
-
-
创建异常类:只需要定义一个派生于Exception的类,或者派生于Exception的子类
-
-
捕获异常
-
捕获异常
-
如果异常发生时没有进行捕获,程序就会终止执行并在控制台输出异常信息
-
要捕获一个异常,需要设置try/catch语句
try{
code
}
catch(ExceptionType e){
code
-
如果在try语句块出现了catch子句中说明的异常类,那么程序会跳过try语句块的其余代码,执行catch语句中的处理器代码
-
如果try语句中没有出现异常,catch语句会被跳过
-
如果方法中的任何代码出现了在catch语句中没有声明的异常,那么该方法会立即退出
-
如果超类没有声明抛出异常,那么对于所有在子类中可能出现的异常都必须进行捕获
-
-
捕获多个异常
-
一个try语句可以捕获多个异常类型,并对不同类型的异常进行捕获,可以为每个异常类型使用一个单独的catch语句
-
使用异常对象的getMessage方法可以获得与异常对象本身有关的信息
-
-
再次抛出异常与异常链
-
在catch子句中可以再次抛出异常,目的是改变异常的类型
-
-
finally子句
-
不管是否有异常被捕获, finally 子句中的代码都被执行
-
可以在finally子句中关闭资源或执行未处理的代码
-
-
使用异常机制的技巧
-
异常处理不能代替简单的测试:捕获异常花费的时间多
-
不要过分地细化异常:会使代码了膨胀
-
利用异常层次结构
-
不要压制异常
-
-
-
使用断言
-
断言的概念
-
断言机制:允许在测试期间向代码插入检查语句
-
关键字assert,两种形式:assert 条件 assert 条件:表达式
-
assert会对条件进行检测,如果结果为false,则抛出一个 AssertionError 异常。表达式将被传人 AssertionError 的构造器, 并转换成一个消息字符串。
-
-
启用和禁用断言
-
在默认情况下, 断言被禁用。 可以在运行程序时用 - enableassertions 或 - ea 选项启用
-
选项 disableassertions 或 - da 禁用某个特定类和包的断言
-
系统类需要使用 - enablesystemassertions / esa 开关启用断言
-
-
使用断言完成参数检查
-
断言失败是致命的 、 不可恢复的错误 。
-
断言检查只用于开发和测阶段
-
-
-
记录日志
-
调试技巧
泛型程序设计
-
为什么使用泛型程序设计
-
类型参数的优点
-
避免了强制类型转换
-
使程序具有更好的可读性和安全性
-
-
-
简单定义泛型类
-
泛型类就是具有一个或多个类型变量的类
-
泛型类引入类型变量,用<>括起,泛型类可以有多个类型变量
-
用具体的类型替换类型变量就可以实例化泛型类型
-
-
泛型方法
-
泛型方法是带有类型参数的方法,可以定义在普通类和泛型类中
-
调用一个泛型方法时 在方法名前的尖括号中放人具体的类型
-
-
类型变量的限定
-
可以将类型参数限定为实现了某些接口的类<T extends 接口>
-
-
泛型代码与虚拟机
-
虚拟机没有泛型对象
-
类型擦除:每一个泛型类型都会自动提供一个原始类型,即删去类型参数后的泛型类型名,并将类型变量替换为限定类型 ( 无限定的变量用 Object)
-
翻译泛型表达式:当程序调用泛型方法时 , 如果擦除返回类型, 编译器插入强制类型转换
-
翻译泛型方法
-
类型擦除也会出现在泛型方法中,参数类型被擦除后只留下限定类型
-
类型擦除会与多态发生冲突,需要编译器在类中生成一个桥方法
-
在虚拟机中, 用参数类型和返回类型确定一个方法 。
-
-
-
约束与局限性
-
不能用基本类型实例化类型参数:类型擦除使得泛型类中含有Object类型的域,Object不能存储基本类型的值
-
运行时类型查询只适用于原始类型
-
虚拟机中的对象总有一个特定的非泛型类型 。 因此, 所有的类型查询只产生原始类型 。
-
如instanceof不能测试泛型类,getClass方法返回的也是原始类型
-
-
不能创建参数化类型的数组:原因类型擦除会导致类型错误
-
Varargs警告
-
由于java 不支持泛型类型的数组 ,因此向参数个数可变的方法(实质是一个数组)传递一个泛型类型会得到一个警告
-
可以采用两种方法来抑制这个警告 。 一种方法是为包含调用的方法增加注解 @SuppressWamings("unchecked") 或者在 Java SE 7 中, 还 可 以 用 @ SafeVarargs 直 接 标 注
-
-
不能实例化类型变量:可以通过反射调用 Clasmewlnstance 方法来构造泛型对象
-
不能构造泛型数组:同样可以利用反射
-
泛型类的静态上下文中类型变量无效
-
不能抛出或捕获泛型类的实例
-
-
泛型类型的继承规则
-
通配符类型(略)
-
通配符概念:允许类型参数变化
-
-
反射和泛型
-
泛型Class类
-
Class类是泛型的,String.class实际上是Class<String>的对象(唯一对象)
-
-
使用Class<T>参数进行类型匹配
-
集合
-
java集合框架
-
将集合的接口与实现分离
-
Colletion接口
-
集合类的基本接口是Colletion接口
-
基本方法:add(),iterator()返回迭代器对象
-
-
迭代器(Iterator接口)
-
next()返回下一个元素
-
remove()删除上次调用next()返回的元素
-
调用remove()前必须调用next()
-
-
泛型实用方法
-
Colletion接口声明了许多有用的方法,所有的实现类都必须提供这些方法(具体方法声明见API)
-
-
集合框架中的接口
-
两个基本接口:Colletion和Map
-
List接口:有序集合,可用迭代器访问或使用整数索引访问(随机访问)
-
Set接口:等同于Colletion接口,但不允许增加重复元素
-
-
-
具体的集合
-
链表
-
java中的链表都是双向的
-
ListIterator接口提供了反向遍历链表的方法
-
迭代器并发修改链表会产生异常
-
随机访问耗费时间多
-
-
数组列表
-
随机访问快
-
Vector是同步的,线程安全;ArrayList不是同步的。如果由单线程访问,Vector同步会耗费大量的时间,因此不需要同步时使用ArrayList
-
-
散列表
-
散列表为每个对象计算一个整数(散列码),不同数据域的对象将产生不同的散列码
-
java中散列表由链表实现
-
散列冲突:桶被占满,需要和桶内所有对象比较
-
再散列:当装填因子达到某个值时,当前散列表用双倍的桶数自动进行再散列
-
-
树集
-
树集是有序的
-
要使用树集必须能比较元素,即这些元素必须实现Comparable接口
-
-
队列与双端队列
-
队列可以让人们有效地在尾部添加一个元素, 在头部删除一个元素
-
有两个端头的队列, 即双端队列, 可以让人们有效地在头部和尾部同时添加或删除元素
-
不支持在队列中间添加元素
-
-
优先级队列
-
优先级队列 ( priority queue ) 中的元素可以按照任意的顺序插人, 却总是按照排序的顺序进行检索 。
-
优先级队列使用堆数据结构
-
-
-
映射
-
基本映射操作
-
java中映射的两个实现:HashMap和TreeMap
-
HashMap对键进行散列,TreeMap用键对元素进行排序
-
键必须是唯一的,不能对同一个键存放两个值
-
-
映射视图:键集 、 值集合 (不是一个集 ) 以及键 / 值对集
-
弱散列映射:解决键/值的回收问题
-
-
视图与包装器
-
轻量级集合包装器
-
子范围
-
不可修改的视图
-
同步视图
-
受查视图
-
-
算法
-
遗留的集合
图形程序设计
-
Swing概述
-
Swing是不对等基于GUI工具箱
-
Swing拥有丰富便捷的用户界面元素集合
-
Swing对底层平台依赖很少,因此与平台相关的bug很少
-
Swing给不同平台用户一致的观感
-
-
创建框架
-
java顶层窗口被称为框架(frame)
-
Swing的框架类是JFrame,扩展于AWT库的Frame类
-
JFrame是极少数不绘制在面板上的Swing组件之一,其修饰部件由用户的窗口系统绘制
-
Swing组件必须由事件分派线程进行配置
-
定义关闭框架的响应动作:setDefaultCloseOperation ( JFrame.EXIT_0N_CL0SE) ;
-
显示框架:setVisible(true)
-
-
框架定位
-
setLocation和setBounds设置框架的位置
-
setlconlmage 用于告诉窗口系统在标题栏、 任务切换窗口等位置显示哪个图标 。
-
setTitle 用于改变标题栏的文字 。
-
setResizable 利用一个 boolean 值确定框架的大小是否允许用户改变
-
框架属性:一个获取 / 设置方法对被称为一种属性
-
确定合适的框架大小
-
-
在组件中显示信息
-
JFrame.add()向框架添加组件
-
绘制组件:JComponent或Jpanl
-
-
详见API
事件处理
-
事件处理基础
-
java将事件信息封装在一个事件对象中,java中所有事件对象派生于java.util.EventObject类
-
不同的数据源产生不同的事件对象
-
AWT事件处理机制的概要:
-
监听器对象是一个实现了特定鉴定器接口的类实例
-
事件源是一个能够注册监听器对象并发送事件对象的对象
-
当事件发生时,事件源将事件对象传递给所有注册的监听器
-
监听器对象利用事件对象中的信息决定如何对事件做出响应
-
-
为了实现ActionListener接口,监听器类必须实现actionPerformed方法,该方法接收一个ActionEvent对象
-
简洁地指定监听器:使用lambda表达式
exitButton.addActionListener (event -> System.exit(O)) ;
-
适配器类
-
捕获事件需要有合适的监听器对象
-
每个AWT监听器接口都有对应的适配器类,适配器类实现了监听器接口的所有方法,但每个方法没有做任何事情
-
可以通过扩展适配器类来指定对某些事件的响应动作 , 而不必实现接口中的每个方法
-
-
-
动作(详见API)
-
鼠标事件(详见API)
-
AWT事件继承层次
-
语义事件和底层事件
-
AWT将事件分为底层事件和语义事件
-
语义事件是表示用户动作的事件
-
底层事件是形成语义事件的事件
-
-
用户界面组件Swing
-
Swing和MVC(模型-视图-控制器)设计模式
-
设计模式
-
设计模式是一种结构化的方法
-
AWT和Swing中使用的设计模式
-
模型-视图-控制器模式
-
容器和组件是 “ 组合 ( composite ) ” 模式
-
带滚动条的面板是 “ 装饰器( decorator ) ” 模式
-
布局管理器是 “ 策略 ( strategy ) ” 模式
-
-
-
模型-视图-控制器模式
-
MVC模式实现三个独立的类:
-
模型:存储内容
-
视图:显示内容
-
控制器:处理用户输入
-
-
模型必须实现改变内容和查找内容的方法 。模型是完全不可见的
-
-
-
布局管理概述(详见API)
-
边框布局(JFrame默认布局)
-
流布局(Jpanel默认布局)
-
网格布局
-
-
文本输入
-
文本域(JTextFeild):只允许接受单行文本输入
-
文本区(JTextArea):能接受多行输入
-
JPassword :也只能接收单行文本的输人, 但不会将输入的内容显示出来 。
-
标签和组件
-
滚动窗格
-
-
选择组件
-
复选框(JCheckBox)
-
单选钮(JRadioButton)
-
边框
-
组合框
-
滑动条
-
-
菜单
-
菜单创建:
-
创建菜单栏:new JMenuBar()
-
创建菜单对象; new JMenu()
-
将菜单添加到菜单栏
-
向菜单对象添加菜单项,分隔符和子菜单
-
-
-
对话框(详见API)
部署Java应用
-
JAR文件
-
Java归档文件(JAR)c用于将应用程序打包,JAR文件可以既可以包含类文件,包含其他类型的文件。
-
JAR 文件是压缩的 ,使用了大家熟悉的 ZIP 压缩格式
-
创建JAR文件:jar options File File2 ...
-
清单文件:用于描述归档特征,清单文件被命名为MANIFEST.MF,位于META-INF子目录(清单文件的最后一行必须以换行符结束)
-
可执行JAR文件:使用 jar 命令中的 e 选项指定程序的入口点或在清单中指定应用程序的主类
-
并发
-
什么是线程
-
使用线程给其他任务提供机会
-
将耗时的任务放置在独立的线程中
-
在单独线程中执行任务的过程:
-
将任务代码移到实现了Runnable接口类的run方法中(Runnable是函数式接口,可以使用lambda表达式建立实例)
Runnable r = ()-> { task code } ;
-
由Runnable创建一个Thread对象
Thread t = new Thread(r);
-
启动线程
t.start();
-
-
也可以通过构建Thread类的子类定义一个线程(不推荐)
-
不要调用Thread类或Runnable类对象的run方法。直接调用run方法只会执行同一个线程中的任务,不会创建新线程。应该调用Thread.start()方法,该方法创建一个执行run方法的新线程
-
-
-
中断线程
-
线程终止:线程的run方法执行方法体中的最后一条语句后,并经由执行return语句返回时;或出现了在方法中没有捕获的线程
-
没有可以强制线程终止的方法,可以使用interrupt方法请求终止线程
-
每个线程都具有boolean的中断状态,当对一个线程调用 interrupt 方法时, 线程的中断状态将被置位
-
如果线程被阻塞就无法检测中断状态
-
对InterruptedException的处理:
-
在 catch 子句中调用 Thread.currentThread ().interrupt() 来设置中断状态
-
用 throws InterruptedException 标记你的方法, 不采用 try 语句块捕获异常
-
-
-
线程状态
-
线程有以下6种状态:
-
New(新创建):当用new操作服创建一个线程时,该线程还没有开始运行,状态为New
-
Runnable(可运行):一旦调用start方法,线程就处于Runnable状态。
-
一个可运行线程可能正在运行也可能没有运行(取决于操作系统给线程提供运行的时间)
-
一旦一个线程开始运行 , 它不必始终保持运行 。 事实上, 运行中的线程被中断 , 目的是为了让其他线程获得运行机会
-
线程调度由操作系统决定
-
-
Blocked(被阻塞):当线程试图获取一个对象的内部锁而该锁被其他线程占有时,线程进入阻塞状态;当其他线程释放该锁,并且线程调度器允许本线程获得它的时候,该线程将变成非阻塞状态
-
Waiting(等待):当线程等待另一个线程通知调度器一个条件时, 它自己进入等待状态
-
当线程处于被阻塞或等待状态时 , 它暂时不活动 。 它不运行任何代码且消耗最少的资源 。 直到线程调度器重新激活它
-
-
Timed waiting(计时等待):调用有超时参数的方法将导致线程进人计时等待
-
Terminated(被终止):
-
因为run方法正常退出而自然死亡
-
因为一个没有捕获的异常终止了run方法而意外死亡
-
-
-
-
线程属性
-
线程优先级:每个线程都有一个优先级,一个线程默认继承父线程的优先级;使用setPriority方法设置优先级
-
守护线程:线程调用stDaemon方法可以转换成守护线程,守护线程的唯一用途是为其他线程提供服务
-
未捕获异常处理器
-
线程的run方法不能抛出任何受检异常,但非受检异常会导致线程终止
-
在线程死亡之前, 异常被传递到一个用于未捕获异常的处理器 。该处理器必须属于一个实现 Thread.UncaughtExceptionHandler 接口的类
-
-
-
同步
-
当两个线程存取相同的对象并调用了修改该对象状态的方法时,这种情况成为竞争条件
-
锁对象
-
防止代码块受并发访问的干扰的两种方法:
-
synchronized关键字:自动提供锁以及相关条件
-
ReentrantLock 类:创建ReentrantLock实例,lock()方法封锁锁对象,unlock()方法释放锁对象
-
-
锁是可重入的,线程可以重复地获得以拥有的锁
-
锁保持一个持有计数 (holdcount) 来跟踪对 lock 方法的嵌套调用
-
公平锁:优先选择等待时间最长的线程
-
-
条件对象
-
条件对象用于管理已经获得锁但是不能做有用工作的线程
-
条件对象在条件不满足时调用await()方法,阻塞当前线程并释放锁,直到另一个线程调用同一条件的signalAll方法(激活)
-
如果没有线程唤醒等待的线程,会出现死锁
-
调用 signalAll 不会立即激活一个等待线程 。 它仅仅解除等待线程的阻塞,仍要竞争锁
-
-
synchronized 关键字
-
java对象自带内部锁,
-
如果一个方法用 synchronized 关键字声明, 那么对象的锁将保护整个方法
-
内部对象锁只有一个相关条件:wait()方法添加线程到等待集,notifyAll()/notify()解除阻塞
-
可以将静态方法声明为synchronized,该方法会或得相关类的对象内部锁
-
内部锁的局限:
-
不能中断一个正在试图获得锁的线程
-
试图获得锁时不能设定超时
-
每个锁仅有单一的条件
-
-
-
同步阻塞:通过同步阻塞可以获得锁
synchronized ( obj )//同步阻塞的语法
{
}//获得obj的锁
-
监视器概念
-
监视器的特性
-
监视器是只包含私有域的类
-
每个监视器的对象有一个相关的锁
-
使用该锁对所有的方法进行加锁
-
该锁可以有任意多个条件
-
-
-
Volatile域:volatile 关键字为实例域的同步访问提供了一种免锁机制 。 如果声明一个域为 volatile ,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的 。
-
final变量:当域声明为final时可以安全地被访问,
-
原子性
-
死锁
-
线程共享变量
-
锁测试与超时
-
tryLock方法试图申请一个锁,成功获得返回true,失败返回false并且线程可以立即离开去做其他事情
-
在调用tryLock时可以使用超时参数
-
lock不能被中断,如果一个线程在等待获得一个锁时被中断 , 中断线程在获得锁之前一直处于阻塞状态
-
调用带有用超时参数的 tryLock , 那么如果线程在等待期间被中断, 将抛出InterruptedException 异常
-
-
读/写锁
-
java.util.concurrent.locks包的两个锁类:ReentrantLock类和ReentrantReadWriteLock类
-
ReentrantReadWriteLock允许对读者线程共享访问,写者线程互斥访问
-
使用读/写锁的步骤;
-
构造一个ReentrantReadWriteLock对象 :
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock ();
-
抽取读锁和写锁 :
private Lock readLock = rwl.readLock();
rivate Lock writeLock = rwl.writeLock();
-
对所有的获取方法加读锁 :
public double getTotalBalance()
{
readLock.lock();
try { . . }
}
finally { readLock.unlock() ;
}
-
对所有的修改方法加写锁 :
public void transfer ( ... )
{
writeLock.lock();
writeLock
try{ ... }
finally
{.unlock () ; }
}
-
-
-
-
阻塞队列
-
使用一个或多个队列解决线程问题
-
生产者线程向队列插人元素, 消费者线程则取出它们 。
-
当试图向队列添加元素而队列已满 , 或是想从队列移出元素而队列为空的时候, 阻塞队列 ( blocking queue ) 导致线程阻塞 。
-
-
线程安全的集合
-
高效的映射、集和队列
-
java . util . concurrent 包提供了映射 、 有序集和队列的高效实现 : ConcurrentHashMap 、ConcurrentSkipListMap > ConcurrentSkipListSet 和 ConcurrentLinkedQueue 。
-
-
映射条目的原子更新
-
使用 replace 操作 , 它会以原子方式用一个新值替换原值
-
-
-
Callable与Future
-
Callable类似于Runnable,但是有返回值
-
Future 保存异步计算的结果
-
-
执行器
-
如果程序中创建了大量的生命期很短的线程 , 应该使用线程池
-
使用线程池可以减少并发线程的数目
-
执行器 ( Executor ) 类有许多静态工厂方法用来构建线程池
-
将 Runnable 对象交给线程池 , 就会有一个线程调用 run 方法。 当 run 方法退出时 , 线程不会死亡, 而是在池中准备为下一个请求提供服务 。
-
线程池
-
newCachedThreadPool方法构建了一个线程池 , 对于每个任务, 如果有空闲线程可用 , 立即让它执行任务, 如果没有可用的空闲线程, 则创建一个新线程 。
-
newFixedThreadPool方法构建一个具有固定大小的线程池。如果提交的任务数多于空闲的线程数, 那么把得不到服务的任务放置到队列中。当其他任务完成以后再运行它们。
-
newSingleThreadExecutor 是一个退化了的大小为 1 的线程池: 由一个线程执行提交的任务, 一个接着一个 。
-
调用submit方法将Runnable对象或Callable对象提交给 ExecutorService
-
当用完一个线程池的时候, 调用 shutdown启动线程池的关闭序列
-
-
-
同步器