《Java核心技术 卷Ⅰ 基础知识》点击下载
《Java API 6中文文档》点击下载
- java白皮书的关键术语
- 简单性
- 面向对象
- 简单讲,面向对象设计是一种程序设计技术,它将重点放在数据(即对象)和对象的接口上。
- 网络技能
- Java的网络能力强大,网络编程简单
- 健壮性
- Java在设计时,投入了大量的精力进行早期的问题检测,后期动态的检测,并消除了有出错倾向的状态,Java和c++最大的不同在于,Java采用的指针模型可以消除重写内存和损坏数据的可能性
- 安全性
- Java适用于网络/分布式环境。在安全方面,使用Java可以构建防病毒,放篡改的系统
- 体系结构中立
- 编译器生成一个体系结构中立的目标文件格式,这是一种编译过的代码,只要有Java运行时系统,就可以在许多处理器上运行
- 可移植性
- 解释型
- Java解释器可以在任何移植了解释器的机器上执行Java字节码。由于链接是一个增量式且轻量级的过程,开发变得更加快捷
- 高性能
- 字节码文件的性能已经很不错了,在特定情况下,字节码(在运行时刻)可以快速翻译成这个应用程序的特定CPU的机器码
- 多线程
- 提高了交互响应和实施行为
- 多动态
- 可以适应不断变化发展的环境
只强调一点:Windows环境中,强烈不建议使用默认安装目录
- Java虚拟机从制定类中的main方法开始执行。
- 方法调用通用语法:
- 注释
- 单行注释 //
- 多行注释 /* */
- 文档注释
- 以/**开头
- 以 */结尾,可以帮助最大生成文档
- 数据类型
- Java是强类型语言,也就是说,每一个变量必须声明一种类型,Java没有任何无符号类型
- 8中基本类型
- 4钟整型
- 浮点类型
- 用于表示有小数部分的数值
double表示这种类型的数值精度是float的两倍
float后缀F,例如3.14F
double后缀D,例如3.14D
- 用于表示有小数部分的数值
- char类型
- char类型表示单个字符,通常用来表示字符常量
- 通常使用单引号,例如:’A’是编码为65所对应的字符常量。而”A”是包含字符A的字符串
转义字符序列符都可以出现在字符常量或者字符串的引号内,例如:’\u2111’ 或 “hello\n”
- boolean类型
- 布尔类型有两个值,true和false,用于逻辑判断,整型和布尔型之间不能相互转换
- 4钟整型
- 变量
- 大小写敏感,包含字符下划线美元符号,长度没有限制
- 声明:可以逐一声明,也可多个一起声明。建议使用前者,提高代码的可读性,声明时,尽量靠近变量第一次使用的地方
- 初始化:变量名 = 数值
- 常量
- 关键字final指示常量,常量是能赋值一次不能更改,常量使用全大写
- 例如:
final double PAI = 3.1415926
- 运算符
- +
- -
- *
- /
- %
- 自增自减运算符
- ++
- –
- 关系运算符和Boolean运算符
- ==
- !=
- >
- <
- >=
- <=
- &&
- ||
- !
- 位运算符
- &
- |
- ~
- 算术移位
- >>
- 将二进制位右移操作
- <<
- 将二进制位左移操作
- >>>
- 用0填充高位
- >>
- 数值类型之间的转换
实线箭头表示无信息丢失的转换,虚线箭头表示精度可能损失 - 强制类型转换
double x = 9.99;int nx = (int) x;
- 括号与运算符级别
- 有括号先算括号里面的
- 有括号先算括号里面的
- 枚举类型
enum student{name,id,sex,age};
student name=student.name;
- 字符串
- 从概念上讲字符串就是Unicode字符序列,例如:”Java\u2122”,有5个Unicode字符J a v a ™
- 类String中提供了很多字符串的方法,例如子串的获取和拼接子串….
- 控制流程
- 条件语句
if(condition){statement}
if(condition){statement1}else{statement2}
- 循环
while(condition) {statement}
- 条件成立执行循环体,不成立跳出循环
do{statement} while(condition)
- 先执行循环体,在判断条件,成立继续执行,不成立跳出循环
for(定义一些东西;条件;一次循环结束后执行){statement}
- 多重选择
- switch语句
- break continue
- break结束本次循环
- continue结束本层循环
- 条件语句
-
- 数组时一种数据结构,用来存储同一类型值的集合
- 声明:
int[] a;
或int a[];
,一般使用前者 - 创建数组:
int[] a = new int[100];
- 这条语句创建一个可以存储100个整数的数组,数组的长度不要求时常量,
new int[n]
,会创建一个长度为n的数组
- 这条语句创建一个可以存储100个整数的数组,数组的长度不要求时常量,
- 数组的下标是0~99,创建一个数字数组时,所有元素都被初始化为0,Boolean中初始化为false,对象数组初始化为null
- 数组一旦创建,就不能改变其大小
- for each循环
- 基本语句格式:
for(variable:collection) {statement}
- 例如
for(int element:a){System.out.println(element);}
,这个循环,将打印出a中的每一个元素 - 传统循环打印数组
- 利用String类中的方法打印
System.out.println(Arrays.toString(a));
- 基本语句格式:
- 数组的初始化
int[] a = {1,2,3,4,5,6};
- 数组长度允许为0,但0和null不同
- 数组拷贝
- Java中允许将一个数组变量拷贝给另一个数组变量,这时两个变量将引用同一数组
int[] b = a;
- 也可以对其重新赋值
b[5] = 7;
- 利用数组类的方法拷贝
int[] b = Arrays.copyOf(a,a.length);//第二个参数时新数组的长度,可以增加,也可变小
- 如果长度大于原始数组,多余的整型补0,布尔型补false。长度小于原始数组,则只拷贝最前面的元素
- 命令行参数
public static void main(String[] args)
- 这个main方法的参数,表明接收一个字符串数组,也就是命令行参数
- 也即是说,在命令行中执行
java Message g c w
,其中Message是包含那个主方法的一个类,那么args数组中包含下列内容:args[0]=”g”; args[1]=”c”; args[2]=”w”
- 数组排序
- 可以使用很多算法进行排序
- 也可以使用Arrays中的sort方法
- 二维数组
- 初始化:
int[][] a = {{1,2,3},{4,5,6},{7,8,9}};
- 访问:
a[][]
- 遍历:嵌套for循环,for each并不能自动处理二维数组的每个元素,也可以使用
Arrays.deepToString(a);
- 理解:Java实际没有多维数组,只有一维数组,多维数组被解释为:数组的数组
- 初始化:
-
- 面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序总很多对象来自标准库,还有一些是自定义的。从根本上讲,只要对象能满足要求,就不必关心其功能的具体实现过程。在OOP中,不必关心对象的具体实现,只要满足用户需求即可
- 面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序总很多对象来自标准库,还有一些是自定义的。从根本上讲,只要对象能满足要求,就不必关心其功能的具体实现过程。在OOP中,不必关心对象的具体实现,只要满足用户需求即可
-
- 类(class)是构造对象的模板或者蓝图。由类构造对象的过程称为创建类的实例
- 类之间的关系:依赖;聚合;继承
- 类的使用:使用类库中的类,使用自定义的类
- 封装:从形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。实现封装的关键在于绝对不能让类中的方法直接的访问其他类的实例域。
- 实例域:对象中的数据
- 方法:操纵数据的过程
- 状态:实例域值的集合
- 类设计技巧
- 保证数据私有
- 对数据初始化
- 不要在类中使用过多的基本类型
- 不是所有的域都需要独立的域访问器和域更改器
- 将职责过多的类进行分解
- 类名,方法名,做到见名知意
- 类(class)是构造对象的模板或者蓝图。由类构造对象的过程称为创建类的实例
- 对象:对象的行为, 状态,标识,静态变量,静态常量,静态方法,访问权限
- 方法参数
- 两种类型
- 基本数据类型(数字,布尔值)
- 对象引用,参数的命名要见名知意
- 使用情况
- 一个方法不能修改一个基本数据类型的参数
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象
- 两种类型
- 对象构造
-
- 在一个类中,具有相同名字,不同参数(个数或者类型)的方法,这种情况叫做重载
-
- 是指在子类中对父类的某方法进行重新定义,其子类的该方法名以及参数位置和个数均与父类相同,从而在调用子类的该方法时,不会执行父类的方法.如果在父类中以final定义的方法,在子类中无法重写.
- 构造器:
- 和类名相同的一个方法。其实就是构造方法,只不过Java中没有构造方法的概念
-
- 包
- Java借助包将类组织起来。使用包也可以方便组织自己的代码
- 使用包的主要原因是确保类名的唯一性
- 类的导入
- 一个类可以使用所属包的所有类,以及其他包的公有类。只要使用import语句引入该类即可。
- 如果在使用中出现了,不同包的相同类名的类都需要使用,这时,就必须在类名前加上完整的包名
- 静态导入
- import不仅可以导入类,还增加了导入静态方法和静态域的功能
- 文档注释:
- 每个文档注释在标记之后紧跟着自由格式文本,由@开始,第一句应该是概要行的句子,javadoc使用程序自动的将这些句子抽取出来形成概要页。自由格式文本中可使用HTML修饰符
- 类注释
- 方法注释
- @param 变量描述
- @return描述
- @throws类描述
- 域注释
- 通用注释
- @author 姓名
- @version 文本
- @since 文本
- 引入版本特性描述
- @deprecated 文本
- @see 引用
- Java中使用extends关键字来实现继承关系。
- Java中一个类只能继承一个类
- “is-a”规则:用来判断,是否应该设计为继承关系的简单规则,它表明,子类的每个对象也是父类的对象,例如每个manager都是Employee(见多态的例子)
- 被继承的类称为超类,基类,或者父类,继承的类称为子类,派生类,或者孩子类。
- 所以在设计类的时候,应该把通用的功能放到父类中,具有特殊用途的放到子类中
-
- 如果子类重写了父类的某个方法,如何在子类中调用父类的这个方法?
- 使用super关键字,
super.方法名();
即可,例如super.getSalary();//这个表达式返回的值就是父类方法getSalary()返回的值
- super不是一个对象的引用,它只是一个指示编译器调用父类方法的特殊关键字
- 使用super关键字,
- super在构造器中的应用
- 如果子类有构造器。那么由于子类的构造器不能访问父类的私有域,所以不许通过父类的构造器来对这部分私有域初始化
- 即
super(n,s,year,mouth,day);
这条语句必须放到子类构造器的第一句。 - 如果子类构造器没有显式的调用父类构造器,那么默认调用没有参数的构造器。
- 如果子类没有调用,且父类构造器中没有不带参数的构造器,那么会报错
- 如果子类重写了父类的某个方法,如何在子类中调用父类的这个方法?
-
- this关键字:this指的就是当前对象(类的实例)。
- 有以下3种作用
- (1)在一个方法内,this调用本类中的属性,也就是类中的成员变量;
- (2)在另一个方法内,this调用本类中的其他方法;
- (3)在另一个方法内,this调用本类中的其他构造方法,调用时要放在构造方法的首行。
多态
先看一个例子
Employee.javapublic class Employee { private String name; private double salary; public Employee(String name,double salary){ this.name = name; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
Manger.java
public class Manager extends Employee { private double bounce; public void setBounce(double bounce){ this.bounce = bounce; } public Manager(String name, double salary) { super(name, salary);//实例化构造器 } public double getSalary(){ double baseSalary = super.getSalary();//调用父类方法 return baseSalary+bounce; } public static void main(String[] args) { Manager boos = new Manager("sjt",9000); boos.setBounce(1000); Employee[] staff = new Employee[2]; staff[0] = boos; staff[1] = new Employee("sjt2",9000); for(Employee e:staff){ System.out.println(e.getName()+ " "+ e.getSalary()); } } } //子类数组的引用可以转换为父类数组的引用,而不需要强转 /*例如: Manager[] managers = new Manager[10]; Employee[] staff = managers;//这样做是ok的 */
一个对象变量(例如 e)可以指示多种实际类型的现象就称为 多态
变量e既引用了employee类对象,也可以引用employee类的任何一个子类的对象
能够在运行时自动的调用那个方法的现象称为 动态绑定
对象调用的执行过程:- 编译器查看对象的声明类型和方法名
- 接下来,编译器将查看调用方法时提供的参数类型。
- 如果是private方法,static方法,final方法,或者是构造器,那么编译器将可以准确的直到应该调用哪个方法,这种调用方式称为静态绑定
- 当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法。
- final
- final可以修饰类和方法,如果修饰类,那么这个类不能以被扩展,即不能被继承。如果修饰方法,那么子类不能覆盖这个方法,
- 强转类型
Manager boss = (Manager) staff[0];//和基本类型的转换类似
- 需要注意的是,在转换之前,应该使用
instanceof
检查是否可以成功转换
if(staff[1] instanceof Manager){boss = (Manger)staff[1];}
- 如果不检查,系统会报告错误,并产生ClassCastException异常,如果没有捕获该异常,那么程序就会停止运行。
- 只能在继承层次内进行转换
- 继承层次:有“父子”关系的类。或者子孙关系等。通俗的讲,就是一家子的人才能转换
抽象类:
- 包含一个或多个抽象方法的类本身必须被声明为抽象的
抽象方法:抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰
例如:
abstract class A{//定义一个抽象类 public void fun(){//普通方法 System.out.println("存在方法体的方法"); } public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰 }
抽象类不能被实例化,即不能创建该类的对象。即使一个类中没有抽象方法,也可以将其定义为抽象类,同样,该类不可以实例化。
抽象类的子类必须覆盖父类的所有抽象方法
- 访问修饰符
- private:仅对本类可见
- public:对所有类可见
- protected:对本包和所有子类可见
- 不使用修饰符:对本包可见
-
- Object类是所有类的父类,Java中所有类都有它扩展而来,但并不需要继承它
- 可以使用Object类型的变量,引用任何类型的对象
Object obj = new Employee("sjt",123);
- 在Java中,只有基本类型,不是对象,例如:数值,字符,布尔类型的值都不是对象。所有的数组类型,不管是对象数组还是基本类型数组都是扩展与Object类
- equals方法:检测一个对象是否等于另外一个对象,即判断两个对象的引用是否相同
- 对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等
- 在标准的Java库中包含150多个equals方法,可以查看java.sql.Timestamp类的API文档
- hashCode方法:散列码(hash code)是由对象导出的一个整型值,散列码是没有规律的
- 每个对象都有一个默认的散列码,其值为对象的存储地址
- toString方法:返回标识对象值的字符串。
- Object类定义了toString方法,用来打印输出对象所属类名和散列码,
System.out.println(boss);
- toString方法是一种非常有用的调试工具,在标准类库中,许多类都定义了toString方法,以便用户能获得一些有关对象状态的必要信息
- 强烈建议,为自定义的每个类增加一个toString方法
- Object类定义了toString方法,用来打印输出对象所属类名和散列码,
- 泛型数组列表
- 动态数组的实现,ArrayList是一个采用类型参数的泛型类。例如:
ArrayList<Employee> staff = new ArrayList<>();
如果数组大小已经确定,也可以在()中指定初始容量,例如:ArrayList<Employee> staff = new ArrayList<>(100);
- 使用add方法可以将元素添加到数组列表中
staff.add(new Employee("sjt3",123)
如果调用add且内部数组已经满了,数组列表就将自动的创建一个更大的数组,并将所有的对象,从较小的数组中拷贝到较大的数组中 - 一旦确定数组大小不在发生变化时,就可以调用trimToSize方法,这个方法将存储区域的大小调整为当前元素数量所需要的存储空间数目,垃圾回收器将回收多余的存储空间
- 访问数组列表元素:
staff.set(i,harry);//第i个元素替换为harry
这个set方法只能替换数组中已存在的元素内容Employee e = staff.get(i);
get可以获取第i个元素的值。没有泛型类时,原始的ArrayList类提供的get方法只能返回Object,因此使用get时,必须对返回值进行强制转换,即Employee e = (Employee)staff.get(i);
- 动态数组的实现,ArrayList是一个采用类型参数的泛型类。例如:
- 对象包装器和自动装箱
- 有时,需要将int这样的基本类型转换为对象,所有基本类都有与之对应的类,这些类称为包装器。例如:int(Integer);long(Long);float(Float);double(Double);short(Short);byte(Byte);char(Character);boolean(Boolean).
- 对象包装器是不可变的。这个类也是final类。
ArrayList<Integer> list = new ArrayList<>();
这样就声明了一个Integer对象的数组列表list.add(3);
会被自动变换为list.add(Integer.valueOf(3));
,这种变换就成为自动装箱- 相反的,当一个Integer对象赋值为一个int值时,就会自动拆箱,也就是说,编译器将
int n = list.get(i)
翻译成int n = list.get(i).intValue();
- 枚举类
public enum Size{s,m,l,xl};//可以添加一些构造器,方法,域
- 在比较两个枚举类型的值的时候,不要使用equals而要使用“==”
- 所有枚举类都是Enum类的子类,继承的方法中,最常用的是toString,可以返回枚举常量名。
- 反射
- 反射库,提供了一个非常丰富且精心设计的工具集,以便编写能够动态操作Javadiamagnetic的程序。
- 能够分析类能力的程序称为反射。反射机制可以用做以下几个方面:
- 运行中分析类的能力
- 运行中查看对象
- 实现通用的数组操作代码
- 利用Method对象,这个对象很想C++中的函数指针
- 反射,是一种强大且复杂的机制。反射的主要使用者是工具的构造者
- 继承设计的技巧
- 将公共操作和域放在父类
- 不要使用受保护的域
- 使用继承实现“is-a”关系
- 除非所有继承的方法都有意义,否则不要用继承
- 在覆盖方法时,不要改变预期的行为
- 使用动态,而非类型信息
- 不要过多的使用反射
-
- 接口主要用来描述类有什么功能,而不给出每个功能的具体实现。一个类可以实现一个或者多个接口(实现多个时,用逗号隔开即可)
- 接口不是类,类是方法和属性的容器,接口也不可以被实例化。但是可以声明接口变量,而接口变量必须引用实现了接口的类对象。
- 接口中的方法默认为public,
- 接口绝不能不含有实例域,但是可以包含常量,并且接口中的域将被自动的设置为public static final。
- 不能在接口中实现方法
- 一个类实现接口需要两个步骤
- 将类声明为实现给定的接口,需要使用implements关键字
- 对接口中的所有方法进行定义
- 接口和抽象类
- 为什么不把接口直接设计成抽象类呢??
- 原因很简单:一个类只能有一个父类,但可以有多个接口,这样做,类的功能更加灵活。
- 为什么不使用多继承??
- 原因:使用多继承会使程序的效率变低,语言本身变复杂。
- 其实接口,变相的实现了多继承,而且避免了复杂和效率的问题
- 为什么不把接口直接设计成抽象类呢??
- 接口和回调
- 回调是一种程序设计模式,在这种模式中,可以指出某个特定事件发生时应该采取的动作
对象克隆
对象克隆指的是创建一个新对象,且新对象的状态和原始对象的状态相同。当对克隆的新对象修改时,不会影响原始对象的状态
Employee original = new Employee("sjt",123); Employee copy = original.clone();//clone方法是Object类的一个方法 copy.raiseSalary(10); //对象克隆需要实现Cloneable接口
是不是这么简单呢?答案是否定的。这种拷贝,只能拷贝域,但如果对象中包含了子对象的引用,这样会使两个域引用一个对象,所以这部分信息他们就是公用的。这并不符合对象克隆的概念。这种拷贝称为浅拷贝
- 如果说,那个子对象是不可变的,那么浅拷贝也是没有关系的
但一般情况下,子对象都是变化的,所以,要重新定义clone方法,就可以实现深拷贝
对每个类做出以下判断:
- 1)默认的clone方法是否满足要求
- 2)默认的clone方法是否能够通过调用可变子对象的clone得到修补
- 3)是否不应该使用clone
实际上,选项3是默认的,如果要选择1或者2,类必须:
- 1)实现Cloneable接口
- 2)使用public访问修饰符重新定义clone方法
- 注释:在Object类中,clone方法被声明为protected
- 内部类
- 一个类的内部定义一个类,这类就称为内部类。内部类的方法可以访问包含他们的外部类的域。
- 使用内部类的原因:
- 内部类方法可以访问改类定义所在的作用域中的数据,包括私有的数据
- 内部类可以对同一个包中的其他类隐藏起来
- 当想定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
- 代理
- 这是可以实现任意接口的对象,代理是非常专业的构造工具,它可以用来构建系统级的工具
GUI编程
JAR文件
- 在将应用程序打包时,使用者一定希望提供给器一个单独的文件,而不是一个含有大量类文件的目录,,Java归档(JAR)文件就是为此目的而设计的。
- 一个JAR文件既可以包含类文件,也可以包含诸如图像和声音这些其他类型的文件,此外,JAR文件时压缩的,它使用了大家熟悉的ZIP压缩格式
- 清单文件
- 除了类文件,图像和其他资源外,每个JAR文件还包含一个用于描述归档特征的清单文件。
- 清单文件被命名为:MANIFEST.MF,它位于JAR文件的一个特殊META-INF子目录中
- 清单文件最后一行必须以换行符结束
- JAR文件是可以运行的
资源
- 在applet和应用程序中使用的类通常需要使用一些相关的数据文件。例如:
- 图像和声音文件
- 带有消息字符串和按钮的标签的文本文件
- 二进制数据文件
在Java中这些关联文件被称为资源
- 在applet和应用程序中使用的类通常需要使用一些相关的数据文件。例如:
- Java Web Start
- Java Web Start时一项在Internet上发布应用程序的技术。
- Java Web Start应用程序一般通过浏览器发布
- Java Web Start应用程序并不在浏览器窗口内,它将显示在浏览器外的一个属于自己的框架中
- Java Web Start应用程序不使用浏览器的Java实现。
- 数字签名应用程序可以被赋予访问本地机器的任意权限,未签名的应用程序只能运行在沙箱中它可以阻止具有潜在危险的操作
- 沙箱是一种被限制的执行环境,在这种环境中,代码不能修改或查看用户系统
- 要想在沙箱外运行,Java Web Start应用程序的JAR文件必须进行数字签名
- applet
- applet是一种包含在HTML网页中的Java应用程序。HTML网页必须告诉浏览器需要加载哪个applet以及applet放置在页面的哪个位置。
- applet可以处理图像和音频
应用程序首选项存储
- 应用程序的用户通常期待能够自行对应用程序进行配置,并能够将其保存起来,日后再次运行这个运行程序时,将能够读取这些配置
属性映射
是一种存储键/值对的数据结构。有三个特性:
- 键和值都是字符串
- 键/值对可以很容易的写入文件或从文件读出
- 用二级表存放默认值
实现属性映射的Java类被称为Properties
- Preferences API
处理错误
- 如果由于出现错误而使得某些操作没有完成,程序应该:
- 返回到一种安全的状态,并能让用户执行一些其他的命令
- 允许用户保存所有操作的结果,并以适当的方式终止程序
- 常见的错误
- 用户输入错误
- 设备错误
- 物理限制
- 代码错误
异常分类:
- Java中异常对象都是派生于Throwable类的一个实例
- Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。这种情况下,处理通知用户,并尽力使程序安全的终止之外,在无能为力。这种情况很少出现
- Exception层次结构是设计Java程序应该关注的。程序错误导致的异常属于RuntimeException。而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。
- 派生于RuntimeException的异常包含下面几种情况:
- 错误的类型转换
- 数组访问越界
- 访问空指针
- 不是派生于RuntimeException的异常包括:
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的文件
- 视图根据给定的字符串查找Class对象,而这个字符串标识的类并不存在
- Java语言规范将派生于Error类或者RuntimeException类的所有异常称为:未检查异常;将其他异常称为:已检查错误
- 以下四种情况下,应该抛出异常:
- 调用一个抛出已检查异常的方法
- 程序运行过程中发现错误
- 程序出现错误
- Java虚拟机和运行时库出现的内部错误
- 使用throws即可抛出相应的异常
定义自己的异常类
- 定义一个派生于Exception的类,或者派生于Exception子类的类
定义两个构造器。一个默认的,一个带有详细描述信息的。
class FileFormatException extents IOException{ public FileFormatException(){} public FileFormatException(String gripe){ super(gripe); } }
- Java中异常对象都是派生于Throwable类的一个实例
捕获异常
- 使用
try{code} catch(ExceptionType e){handle for this type}
如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,那么:
- 程序将跳过try语句块的其余代码
- 程序将执行catch子句中的处理器代码
如果在try语句块中没有抛出异常,程序将跳过catch语句。
- 如果方法中的任何代码抛出了一个catch子句中没有声明的异常类型,那么这个方法就立即退出
- 使用
捕获多个异常
try{ code } catch(ExceptionType e){ handle for this type } catch(ExceptionType e){ handle for this type } ...
finally子句
try{ code } catch(ExceptionType e){ handle for this type } finally{ //不管是否有异常被捕获,finally子句中的代码都会被执行 } ...
try/catch和try/finally语句块的嵌套使用
try{ try{ code } finally{ ... } } catch(ExceptionType e){ handle for this type } ...
- 使用一场机制的技巧
- 异常处理不能代替简单的测试,即只在异常情况下使用异常机制
- 不要过扽的细化异常
- 利用异常层次结构,捕捉异常要寻找更加适合的异常类
- 不要压制异常
- 检查错误时,“苛刻”要比放任更好,在出错的地方抛出比在后面抛出好
- 不要羞于传递异常(最后两条总结为,早抛出,晚捕获)
- 如果由于出现错误而使得某些操作没有完成,程序应该:
- 使用断言
- 断言机制允许在测试期间向代码中插入一些检查语句。当代码发布时,这些插入的检测语句将会被自动的移走。
- assert 条件:
- assert 条件:表达式;
- 这两种形式都会对条件进行检测,结果为false的话,抛出一个AssertionError异常。第二种形式中,表达式将被传入AssertionError的构造器,并转化为一个消息字符串
- 想要断言一个x是非负数
assert x>=0;
或者将x的实际值传给AssertionError对象,从而可以在后面显示出来assert x>=0:x;
- 需要注意的是断言默认是禁用的。使用时需要开启
- 使用断言完成参数检查
- Java语言中,给出3中处理系统错误的机制
- 抛出一个异常
- 日志
- 使用断言
- 什么时候选择使用断言?
- 断言失败是致命的,不可恢复的错误
- 断言检查只用于开发和测试阶段
- Java语言中,给出3中处理系统错误的机制
- 记录日志
- 我们经常在有问题的代码中插入
System.out.println
方法来观察程序的操作过程,发现错误就得删除这些语句,发现时又得调试。记录日志API就是为了解决这个问题而设计的 - 基本日志
- 高级日志
- 修改日志管理器配置
- 我们经常在有问题的代码中插入
- 使用调试器(Debug)进行调试
- 为什么要使用泛型程序设计?
- 泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用
- 泛型程序设计,划分为3个能力级别。基本级别:仅仅是使用泛型类,大多程序员将停留在这一级别上。。。
定义简单泛型类
public class Pair<T>{ private T first; private T second; public Pair(){first = null;second = null;} public Pair(T first,T second){this.first = first;this.second = second;} public T getFirst(){return first;} public T getSecond(){return second;} public void setFirst(T newValue){first = newValue;} public void setSecond(T newValue){Second = newValue;} }
Java库中,使用变量E表示集合类型的元素类型,K和V分别表示关键字和值类型。T表示任意类型
使用具体的类型替换类型变量就可以实例化泛型类型。例如:Pair<Stirng>
- 泛型方法
- 类型变量的限定
- 泛型类型的继承
- 通配符类型
- 反射和泛型
这章主要讲的是,利用Java类库帮助我们在程序设计中实现传统的数据结构
- 集合接口
- Java最初提供的最常用的数据结构提供的类:Vector,Stack,Hashtable,BitSet,Enumeration接口(用于访问任意容器中各个元素的抽象机制)
- 多线程程序在较低层次上扩展了多任务的概念:一个程序同时执行多个任务。
- 通常每一个任务称为一个线程,它是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序
- 多线程和多进程的区别?
- 本质区别在于每个进程拥有自己的一整套变量,而线程则共享数据。着似乎有些风险,但是这也使线程之间的通信比进程之间的通信更有效,更容易。
- 此外,与进程比较,线程更加“轻量级”。
- 这章主要介绍如何为Java应用程序添加多线程能力