Java知识点总结(二)面向对象

来源:互联网 发布:java graphics 方法 编辑:程序博客网 时间:2024/05/22 14:24

二、面向对象

参考《Java语言程序设计基础篇第10版》总结

9..对象和类

(1)为了方便,本书将包含main 方法的类称为主类(main class)

统一建模语言( Unified Modeling Language,UML) ,UML 类图(UML classdiagram), 或简称为类图(class diagram)。

(2)可以把两个类放在同一个文件中,但是文件中只能有一个类是公共(public) 类。此外,公共类必须与文件同名。

源代码中的每个类编译成.class文件。
(它演示如何通过在一个类中加入main 方法来测试这个类 P275)

(3)构造方法:

构造方法在使用new操作符创建对象的时候被调用。初始化对象。

具备和所在类相同的名字,没有返回值类型、无void,在创建一个对象使用new操作符时调用。

一个类可以不定义构造方法。在这种情况下,类中隐含定义一个 方法体为空的无参构造方法 。这个构造方法称为默认构造方法(default constructor),当且仅当类中没有明确定义任何构造方法时才会自动提供它。

Java 中,数组被看作是对象。数组是用new 操作符创建的。一个数组变量实际上是一个包含数组引用的变量。

(4)匿名对象:

new Circle();

System.out.println( "Area is "+new Circle(5).getArea() );

(5)引用数据域和null值:

数据域也可能是引用型的。

如果一个引用类型的数据域没有引用任何对象,那么这个数据域就有一个特殊的Java值null。

引用类型数据域的默认值是null, 数值类型数据域的默认值是0, boolean 类型数据域的默认值是false, 而char 类型数据域的默认值是 '\u0000'。

但是,Java 没有给方法中的局部变量赋默认值。

null 同true 和false — 样都是一个直接量。true 和false 是boolean 类型直接量,而null是引用类型直接量

(6)垃圾回收:

如图所示,执行完赋值语句c1=c2 之后,c1指向c2所指的同一个对象。c1以前引用的对象就不再有用,因此,现在它就成为垃圾(garbage)。垃圾会占用内存空间。Java 运行系统会检测垃圾并自动回收它所占的空间,这个过程称为垃圾回收(garbage collection)。

如果你认为不再需要某个对象时,可以显式地给该对象的引用变量赋null 值。如果该对象没有被任何引用变量所引用,Java 虚拟机将自动回收它所占的空间。

(7)Java库中类:

①Date类

❶第二章中,System.currentTimeMillis()获得当前时间

❷java.util.Date类:与系统无关的对日期、时间的封装。

java.util.Date date=new java.util.Date();

System.out.println(date.getTime()+" "+date.toString());

②Random类

随机数

❶Math.random() 0.0<=x<1.0 double值

❷java.util.Random类 

创建一个Random对象时,必须指定一个种子或者使用默认的种子。种子是一个用于初始化一个随机数字生成器的数字。

无参构造方法使用当前已经逝去的时间作为种子,创建一个Random对象。

如果这两个Random对象有相同的种子,那么它们将产生相同的数列。

Random r=new Random(3);

System.out.println( r.nextInt(100) );

③Point2D类

javafx.geometry包中

(8)静态

类中常量:final static double PI=3.1415926;

静态方法和静态数据可以通过 引用变量 或 它们的类名 来调用。

想让一个类的所有实例共享数据,静态变量,也称为类变量。

静态变量和方法可以在不创建对象的情况下访问。

使用“ 类名.方法名(参数)” 的方式调用静态方法,使用“ 类名.静态变量” 的方式访问静态变量。这会提高可读性,因为可以很容易地识别出类中的静态方法和数据。

静态方法不能调用实例方法或者访问实例数据域,因为静态方法和静态数据域不属于某个特定的对象

看:

(9)可见性修饰符:

public(无限制的访问) 默认(包私有、包内访问)(包内)private(自己的类内)protected

①包:组织类

package packageName;

如果定义类时没有声明包,就表示把它放在默认包中。

②private:类成员,public:类、类成员。(在局部变量上使用修饰符public 和private 都会导致编译错误)

防止用户创建类的实例:私有构造方法。

(10)数据域封装:

private get、set方法

(11)方法、对象参数:

(12)对象数组:

实际上:引用变量的数组。

当使用new操作符创建对象数组后,这个数组中的每个元素都是默认值为null引用变量

(13)不可变对象和类:

所有数据域都是私有的; 没有修改器方法; 没有一个返回指向可变数据域的引用的访问器方法。(14)变量的作用域:

实例变量和静态变量的作用域是整个类,无论变量是在哪里声明的。

— 个类的实例变量和静态变量称为类变量或数据域,在方法内部定义的变量称为局部变量。

无论在何处声明,类变量的作用域都是整个类。类的变量和方法可以在类中以任意顺序出现。但是当一个数据域是基于对另一个数据域的引用来进行初始化时则不是这样,在这种情况下,必须首先声明另一个数据域。

(15)this引用:

引用对象自身;在构造方法内部用于 调用同一个类的其他构造方法 。

Java 要求在构造方法中,语句this( 参数列表)应在任何其他可执行语句之前出现。

(16)UML类图:

符号+:公共修饰符public

符号-:私有修饰符private

下划线:静态变量、静态方法

指向父类的三角箭头:继承

#:protected修饰符

斜体:抽象方法、抽象类,接口名字和方法名字

虚线和空心三角形 用于 指向接口。

10..面向对象思考

(1)类的抽象(将类的实现和类的使用分离开)和封装。
Java 程序设计涉及对对象的思考,一个Java 程序可以看作是一个相互操作的对象集合。
(2)类的关系:(P316)
关联 聚集(has-a)组合(个对象只归属于个聚集对象)继承
由于聚集和组合关系都以同样的方式用类来表示,我们不区分它们,将两者都称为组合。

(3)将 基本数据类型值 作为对象 处理:

包装类:Boolean、Character、Double、Float、Byte、Short、Integer、Long等。java.lang包中
数值包装类相互之间都非常相似。每个都包含了doubleValue() 、floatValue()、intValue()、longValue()、shortValue() 和byteValue()方法。这些方法将对象“转换”为基本类型值。
既可以用基本数据类型值也可以用表示数值的字符串来构造包装类。例如,newDouble(5.0)、new Double("5.0")、new Integer(5)和new Integer("5")。
包装类没有无参构造方法。所有包装类的实例都是不可变的,这意味着一旦创建对象后,它们的内部值就不能再改变。
每一个数值包装类都有常量MAX_VALUE 和MIN_VALUE。MAX_VALUE 表示对应的基本数据类型的最大值。对于Byte 、Short、Integer 和Long 而言,MIN_VALUE 表示对应的基本类型byte、short 、int 和long 的最小值;对Float 和Double 类而言,MIN_VALUE 表示float 型和double 型的最小正值。
每个数值包装类都会包含方法doubleValue() 、floatValue()、intValue()、longValue()和shortValue()。这些方法返回包装对象的double 、float 、int、long 或short 值。
数值包装类中包含compareTo 方法用于比较两个数值,并且如果该数值大于、等于或者小于另外一个数值时,分别返回1、0、-1。
数值包装类有一个有用的静态方法valueOf(String s)。该方法创建一个新对象,并将它初始化为指定字符串表示的值。
Integer 类中的parselnt 方法将一个数值字符串转换为一个int 值,Double 类中的parseDouble 方法将一个数值字符串转变为一个double 值。每个数值包装类都有两个重载的方法,将数值字符串转换为正确的以10(十进制)或指定值为基数(例如,2 为二进制,8 为八进制,16 为十六进制)(radix)的数值。
String类format方法:(十进制数 转换为 十六进制数)

(4)基本类型 和 包装类型 之间的自动转换:

基本类型 -> 包装类型:装箱 包装类型 -> 基本类型:开箱
自动装箱、自动开箱:
如果一个基本类型值出现在需要对象的环境中,编译器会将基本类型值进行自动装箱;如果一个对象出现在需要基本类型值的环境中,编译器会将对象进行自动开箱。

(5)BigInteger类、BigDecimal类:

Biglnteger 类和BigDecimal 类可以用于表示任意大小和精度的整数或者十进制数
如果要进行非常大的数的计算或者高精度浮点值的计算,可以使用java.math 包中的Biglnteger 类和BigDecimal 类。它们都是不可变的。
Biglnteger 的实例可以表示任意大小的整数。可以使用new Biglnteger(String)和new BigDecimal(String)来创建Biglnteger 和BigDecimal的实例,使用add、subtract、multiple、divide 和remainder 方法完成算术运算,使用compareTo 方法比较两个大数字。
BigDecimal 对象的精度没有限制。如果结果不能终止,那么divide 方法会抛出ArithmeticException 异常。
但是,可以使用重载的divide(BigDecimal d.int scale,int roundingMode)方法来指定尺度和舍入方式来避免这个异常,这里的scale 是指小数点后最小的整数位数。
例:
例:

(6)String类:

String对象是不可改变的。字符串一旦创建,内容不能再改变。
①构造字符串:
❶String message=new String("welcome to java");
❷Java 将字符串直接量看作String 对象。
String message="welcome to java";
❸字符数组创建:
char[] charArray={ 'G','o','o','d',' ','D','a','y' };String message=new String(charArray);
②不可变字符串与限定字符串:
因为字符串在程序设计中是不可变的,但同时又会频繁地使用,所以Java 虚拟机为了提高效率并节约内存,对具有相同字符序列的字符串直接量使用同一个实例。这样的实例称为限定的(interned) 字符串


③字符串的替换和分隔:
String 类提供替换和分隔字符串的方法:

方法replace有好几个版本,它们实现用新的字符或子串替换字符串中的某个字符或子串。

split 方法可以从一个指定分隔符的字符串中提取标识。例:
String[] t="Java#HTML#Perl".split("#");for(int i=0;i<t.length;i++)System.out.print(t[i]+" ");// 显示 Java HTML Perl

④依照模式匹配、替换和分隔:
验证用户输入
正则表达式(regex)是一个字符串,用于描述匹配一个字符串集的模式。可以通过指定某个模式来匹配、替换或分隔一个字符串。
❶(String类 matches方法)
"Java".matches("Java");//true"Java".equals("Java");//true"Java is fun".matches("Java.*");
子串.* 与0 个或多个字符相匹配。
"440-02-4534".matches("\\d{3}-\\d{2}-\\d{4}")
\\d表示单个数字位,\\d{3}表示三个数字位。
❸方法replaceAll,replaceFirst,split可以和正则表达式结合在一起使用。
用字符串NNN替换"a+b$#c"中的$、+、#,然后返回一个新字符串。
String s="a+b$#c".replaceAll("[$+#]","NNN");System.out.println(s);
这里的正则表达式[$+#]表示能够匹配$、+或者#的模式。所以,输出是aNNNbNNNNNNc。
❹下面的语句将字符串分隔为由标点符号分隔开的字符串数组。
String[] t="Java,C?C#,C++".split("[.,:;?]");for(int i=0;i<t.length;i++)System.out.println(t[i]);
这里的正则表达式[.,:;?]指定的模式是指匹配  . 、, 、:、;或者?
这里的每个字符都是分隔字符串的分隔符。因此,这个字符串就被分割成Java、C、C# 和C++, 它们都存储在数组t中。


正则表达式对起步阶段的学生来讲可能会比较复杂。基于这个原因,本节只使用两个简单的模式( .* \\d{3}   [……] )。若要进行进一步的学习,请参照补充材料H。

⑤字符串与数组之间的转换:
❶字符串 -> 数组:
toCharArray方法:
char[] chars="Java".toCharArray();
方法getChars(int srcBegin,int srcEnd,char[] dst,int dstBegin)将下标从srcBegin 到srcEnd-1的子串复制到字符数组dst 中下标从dstBegin 开始的位置:


❷数组 -> 字符串:
构造方法String(char[])或方法ValueOf(char[])
String str=new String( new char[]{ 'J','a','v','a' } );
String str=String.valueOf(new char[]{ 'J','a','v','a' } );


⑥将字符和数值转换成字符串:
❶字符串 -> double/int:
Double.parseDouble(str) Integer.parseInt(str)
❷字符/数字 -> 字符串:
字符串连接符+
重载的 ValueOf方法

String.valueOf(5.44)

⑦格式化字符串:
String类静态方法:format 返回一个格式化的字符串
String.format( format,item1,item2,……,itemk)
例:
String s=String.format("%7.2f%6d%-4s",45.556,14,"AB");System.out.println(s);
注:System.out.printf( format,item1,item2,……,itemk);等价于System.out.print(String.format( format,item1,item2,……,itemk));



(7)StringBuilder和StringBuffer类

StringBuilder 和StringBuffer 类似于String 类,区别在于String 类是不可改变的

— 般来说,只要使用字符串的地方,都可以使用StringBuilder/StringBuffer类。StringBuilder/StringBuffer 类比String 类更灵活。可以给一个StringBuilder 或String- Buffer 中添加、插入或追加新的内容,但是String 对象一旦创建,它的值就确定了。

除了StringBuffer 中修改缓冲区的方法是同步的,这意味着只有一个任务被允许执行方法之外,StringBuilder 类与StringBuffer 类是很相似的。如果是多任务并发访问,就使用StringBuffer, 因为这种情况下需要同步以防止StringBuffer 崩溃。并发编程将在第30 章介绍。而如果是单任务访问,使用StringBuilder 会更有效。StringBuffer 和StringBuilder 中的构造方法和其他方法几乎是完全一样的。本节介绍StringBuilder。在本节的所有地方StringBuilder 都可以替换为StringBuffer。程序可以不经任何修改进行编译和运行。



②修改StringBuilder 中的字符串:
在字符串构建器的末尾追加新内容,在字符串构建器的特定位置插人新的内容,还可以删除或替换字符串构建器中的字符。








③toString、capacity、length、setLength 和charAt 方法:
处理字符串构建器和获取它的属性的方法:

capacity()方法返回字符串构建器当前的容量。
length()方法返回字符串构建器中实际存储的字符数量。
setLength(newLength)方法设置字符串构建器的长度。如果参数newLength 小于字符串构建器的当前长度,则字符串构建器会被截短到恰好能包含由参数newLength 给定的字符个数。如果参数newLength 大于或等于当前长度,则给字符串构建器追加足够多的空字符('\u0000' ), 使其长度length 变成新参数newLength。参数newLength 必须大于等于0。


11..继承和多态

继承

(1)父类、超类、基类 子类、次类、扩展类、派生类
extends
单继承
子类继承它的父类中所有可访问的数据域和方法。父类中的私有数据域在该类之外是不可访问的,不能在子类中直接使用

(2)super关键字:(是指这个super 关键字所在的类的父类)

①调用父类构造方法:
父类的构造方法不会被子类继承。
语句super() 和super (arguments) 必须出现在子类构造方法的第一行,这是显式调用父类构造方法的唯一方式

构造方法可以调用重载的构造方法或父类的构造方法如果它们都没有被显式地调用,编译器就会自动地将super()作为构造方法的第一条语句
(构造方法可以调用重载的构造方法或它的父类的构造方法。
这种调用必须是构造方法的第一条语句。
如果没有显式地调用它们中的任何一个,编译器就会把super()作为构造方法的第一条语句,它调用的是父类的无参构造方法。)

构造方法链:(在任何情况下,构造一个类的实例时,将会调用沿着继承链的所有父类的构造方法。当构造一个子类的对象时,子类构造方法会在完成自己的任务之前,首先调用它的父类的构造方法。如果父类继承自其他类,那么父类构造方法又会在完成自己的任务之前,调用它自己的父类的构造方法。)

注:如果要设计一个可以被继承的类,最好提供一个无参构造方法以避免程序设计错误。

②调用父类的方法:(在方法重写时)

(3)方法重写:
注:Circle 的子类能用语法super.super.toString() 访问定义在GeometricObject 中的toString 方法吗?答案是不能,这是一个语法错误。

与实例方法一样,静态方法也能被继承。但是,静态方法不能被覆盖。如果父类中定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏。可以使用语法:父类名.静态方法名( SuperClassName.staticMethodName) 调用隐藏的静态方法

(4)方法重写(override)与方法重载:
方法重写发生在通过继承而相关的不同类中;方法重载可以发生在同一个类中,也可以发生在由于继承而相关的不同类中
Java重写标注:方法前面放一个@Override

(5)Object 类及其toString()方法

Java中的所有类都继承自java.lang.Object类
如果在定义一个类时没有指定继承性,那么这个类的父类就被默认为是Object

Object 类中的toString() 方法:
public String toString();
调用一个对象的toString()会返回一个描述该对象的字符串。默认情况下,它返回一个由该对象所属的类名、at 符号(@) 以及该对象十六进制形式的内存地址组成的字符串。例:LOan®15037e5
通常,应该重写这个toString 方法,这样,它可以返回一个代表该对象的描述性字符串
注:也可以传递一个对象来调用System.out.println(object)或者System.out.print(object)。这等价于调用System.out.println(object.toString())或者System.out.print(object.toString())。

多态

(6)多态意味着父类的变量可以指向子类对象 
总可以将子类的实例传给需要父类型的参数
使用父类对象的地方都可以使用子类的对象
父类型的变量可以引用子类型的对象

(7)动态绑定:(方法可以在沿着继承链的多个类中实现。JVM 决定运行时调用哪个方法)
声明类型: 一个引用类型变量可以是一个null 值或者是一个对声明类型实例的引用实例可以使用声明类型或它的子类型的构造方法创建
实际类型:被变量引用的对象的实际类。
o调用哪个toString() 方法由o的实际类型决定。这称为动态绑定(dynamic binding)


匹配方法的签名绑定方法的实现是两个不同的问题。
引用变量的声明类型决定了编译时匹配哪个方法。在编译时,编译器会根据参数类型、参数个数和参数顺序找到匹配的方法。
一个方法可能在沿着继承链的多个类中实现。Java 虚拟机在运行时动态绑定方法的实现,这是由变量的实际类型决定的。
(当从引用变量调用实例方法时,该变量的实际类型在运行时决定使用该方法的哪个实现。这称为动态绑定。)

(8)对象转换和instanceof运算符:
对象转换:对象的引用可以类型转换为对另外一种对象的引用
隐式转换、显式转换
一个好的经验是,在尝试转换之前确保该对象是另一个对象的实例。这是可以利用运算符instanceof来实现的
Object m=new Circle();if( m instanceof Circle )System.out.println( ((Circle)m).getDiameter() );

变量m被声明为Object。声明类型决定了在编译时匹配哪个方法。使用m.getDiameter() 会引起一个编译错误,因为Object 类没有getDiameter 方法。编译器无法找到和m.getDiameter()匹配的方法所以,有必要将m转换成Circle 类型,来告诉编译器m也是Circle 的一个实例。

对象成员访问运算符( .)优先于类型转换运算符。使用圆括号保证在点运算符( .)之前进行转换,如:
((Circle)m).getDiameter() 

(9)Object 类的equals方法:

public boolean equals(Object o);
Object 类中equals 方法的默认实现是:
public boolean equals(Object obj){
return (this==obj);
}
这个实现使用== 运算符检测两个引用变量是否指向同一个对象。因此,应该在自己的客户类中重写这个方法,以测试两个不同的对象是否具有相同的内容。
例:重写Circle 类中的equals 方法,基于半径比较两个圆是否相等:
public boolean equals(Object o){if(o instanceof Circle)return radius==((Circle)o).radius;else return this==o;}

(10)ArrayList类:java.util包
ArrayList 对象可以用于存储一个对象列表。
数组一旦创建,它的大小就固定了。Java 提供ArrayList 类来存储不限定个数的对象。

①ArrayList<String> c=new ArrayList<String>();

ArrayList<String> c=new ArrayList<>();

②indexOf方法,如果对象不在这个数组列表中,它返回-1。
③当创建ArrayList 后,它的大小为0。

④注:可以使用java.util.Arrays.sort(array)方法来对一个数组排序。如果要对一个数组列表排序,使用java.util.Collections.sort(arrayList)方法。

⑤foreach循环:

for(elementType element : arrayList){……}


(11)对于列表有用的方法:

数组 -> 数组列表:



排序,最大、最小,随机打乱:


(12)protected 数据和方法:

protected:允许子类访问定义在父类中的数据域或方法,但不允许非子类访问这些数据域和方法



不使用修饰符:允许同一个包里的任何类直接访问类的成员

protected :允许任何 包中的子类同一包中的类访问类的成员

如果想让该类的扩展者使用数据和方法,而不想让该类的用户使用,则把成员声明成protected。

修饰符private 和protected 只能用于类的成员。public 修饰符和默认修饰符(也就是没有修饰符)既可以用于类的成员,也可以用于类。

一个没有修饰符的类(即非公共类)是不能被其他包中的类访问的。

子类可以重写它的父类的protected 方法,并把它的可见性改为public。但是,子类不能削弱父类中定义的方法的可访问性。例如:如果一个方法在父类中定义为public,在子类中也必须定义为public。

(13)防止扩展和重写:

一个被final 修饰的类和方法都不能被扩展。被final 修饰的数据域是一个常数。
有时候,可能希望防止类扩展。在这种情况下,使用final 修饰符表明一个是最终的,是不能作为父类的。(Math 类就是一个最终类)
也可以定义一个方法为最终的,最终方法不能被它的子类重写。

修饰符public、protected、private、      static、 abstract 以及final 可以用在类和类的成员(数据和方法)上,只有final 修饰符还可以用在方法中的局部变量上。方法内的最终局部变量就是常量。


12..异常处理和文本I/O

异常处理

(1)异常:从方法抛出。方法的调用者可以捕获以及处理该异常。
不能用一个整数除以0 (注意,一个浮点数除以0 是不会产生异常的)
不应该让方法来终止程序——应该由调用者决定是否终止程序

(2)方法:throw抛出异常 throw new ArithmeticException("divisor cannot be zero");
throws声明异常public void myMethod() throws Exception1,Exception2,……,Exception{……}
调用者(如main方法中): try-catch块捕获、处理异常
try{//……}catch(ArithmeticException e){ System.out.println("zero");}

被调用的方法通常不知道在出错的情况下该做些什么,这是库方法的一般情况。库方法可以检测出错误,但是只有调用者才知道出现错误时需要做些什么
异常处理最根本的优势就是将检测错误(由被调用的方法完成)从处理错误(由调用方法完成)中分离出来。

P389 :第22 行的语句input.nextLine() 丢弃当前的输入行。所以,用户就可以键入一个新行。

(3)异常类型:
根类:java.lang.Throwable

①Error:系统错误。Java虚拟机抛出。 内部系统错误。
②Exception:异常。 由程序和外部环境所引起的错误,这些错误能被程序捕获和处理
③RuntimeException:运行时异常。通常由Java虚拟机抛出。 程序设计错误。
RuntimeException 、Error 以及它们的子类都称为免检异常。所有其他异常都称为必检异常,意思是指编译器会强制程序员检査并通过try-catch块处理它们,或者在方法头进行声明。
为避免过多地使用try-catch 块,Java 语言不强制要求编写代码捕获或声明免检异常。

(4)关于异常处理的更多知识:


②声明异常:每个方法都必须声明它可能抛出的必检异常的类型。
(因为任何代码都可能发生系统错误和运行时错误,因此,Java 不要求在方法中显式声明Error 和RuntimeException (免检异常))
如果方法没有在父类中声明异常,那么就不能在子类中对其进行继承来声明异常

②通常,JavaAPI 中的每个异常类至少有两个构造方法:一个无参构造方法和一个带String 参数的构造方法。该参数称为异常消息(exceptionmessage), 它可以用getMessage()获取。

③如果在调用的方法链中找不到处理器,程序就会终止并且在控制台上打印出错信息。寻找处理器的过程称为捕获一个异常(catching an exception)。

④在catch 块中异常被指定的顺序是非常重要的。如果父类的catch 块出现在子类的catch 块之前,就会导致编译错误。

⑤Java 强迫程序员处理必检异常。如果方法声明了一个必检异常(即Error 或Runtime Exception 之外的异常),就必须在try-catch 块中调用它,或者在调用方法中声明要抛出异常。


⑦从异常中获取信息:




由于这个方法抛出RuntimeException (免检异常)子类IllegalArgumentException 的— 个实例,所以,如果不使用try 语句,这个测试程序也能编译。如果方法抛出RuntimeException和Error 之外的异常,那么此方法就必须在try-catch 块内调用。

(5)finally:
try{
……
}
catch(TheException ex){
……
}
finally{
……
}
在任何情况下,finally 块中的代码都会执行,不论try 块中是否出现异常或者是否被捕获。
即使在到达finally 块之前有一个return 语句,finally 块还是会执行。
使用finally 子句时可以省略掉catch 块。

(6)何时使用异常:
当错误需要被方法的调用者处理的时候,方法应该抛出一个异常。
如果能在发生异常的方法中处理异常,那么就不需要抛出或使用异常。
不要把异常处理用作简单的逻辑测试。

(7)链式异常:
同原始异常一起抛出一个新异常(带有附加信息)


(8)创建自定义异常类:派生Exception类
而Exception 类扩展自java.lang.Throwable()。 
Exception 类中的所有方法(例如,getMessage()、toString() 和printStackTrace() ) 都是从Throwable 继承而来的。

P403~P405:……;
提示:可以扩展RuntimeException 声明一个自定义异常类吗?可以,但这不是一个好方法,因为这会使自定义异常成为免检异常。最好使自定义异常必检,这样,编译器就可以在程序中强制捕获这些异常。

(不应该使用异常处理代替简单的测试。应该尽可能地使用if 语句来进行简单的测试,将异常处理留作处理那些无法用if 语句处理的场景)

文本I/O

(9)File类:
File 类包含许多获取文件属性的方法,以及重命名和删除文件和目录的方法,不包含创建文件的方法,不包含读写文件内容的方法


在Windows 中目录的分隔符是反斜杠(\)。但是在Java 中,反斜杠是一个特殊的字符,应该写成\\ 的形式(参见表4-5 )
构建一个File 实例并不会在机器上创建一个文件。不管文件是否存在,都可以创建任意文件名的File 实例。
斜杠(/) 是Java 的目录分隔符

(10)文件输入和输出: (向文本文件读/写数据)
使用Scanner 类从文件中读取文本数据,使用PrintWriter 类向文本文件写入数据。

①PrintWriter类:
java.io.PrintWriter:创建一个文件并向文本文件写入数据

如果文件不存在,调用PrintWriter 的构造方法会创建一个新文件。如果文件已经存在,那么文件的当前内容将在不和用户确认的情况下被废弃。
调用PrintWriter 的构造方法可能会抛出某种I/O 异常
System.out 是控制台的标准Java 对象。
必须使用close() 方法关闭文件


②使用try-with-resources 自动关闭资源
自动关闭文件
try(声明和创建资源){
//使用资源来处理文件;
}

资源必须是AutoCloseable 的子类型,比如PrinterWriter, 具有一个close()方法。
资源的声明和创建必须在同一行语句中,可以在括号中进行多个资源的声明和创建

③使用Scanner 读数据:
java.util.Scanner类
Scanner input=new Scanner( new File(filename) );


调用构造方法new Scanner(File) 可能会抛出一个I/O 异常。
没有必要关闭输人文件(第22 行),但这样做是一种释放被文件占用的资源的好方法
可以使用try-with-resources 语法重写该程序

④Scanner类如何工作: P411
标记读取方法,它们会读取用分隔符分隔开的标记。
默认情况下,分隔符是空格。可以使用useDelimiter(String regex)方法设置新的分隔符模式。
next() 方法读取一个由分隔符分隔的字符串,但是nextLine() 读取一个以换行符结束的行

标记读取方法不能读取标记后面的分隔符。如果在标记读取方法之后调用nextLine(),该方法读取从这个分隔符开始,到这行的行分隔符结束的字符。这个行分隔符也被读取,但是它不是nextLine() 返回的字符串部分
详解:

可以使用Scanner 类从文件或者键盘读取数据。也可以使用Scanner 类从一个字符串扫描数据
Scanner input=new Scanner("13 14");//int sum=input.nextInt()+input.nextInt();System.out.println("Sum is"+sum);//Sum is 27

(11)从Web上读取数据:
从 Web上的文件 中读取数据

java.net.URL类:
public URL(String spec) throws MalformedURLException;

用URL 类中定义的openStream() 方法:
Scanner input=new Scanner( url.openStream() );
现在可以从输人流中读取数据了,如同从本地文件中读取一样。

P416 示例学习:Web爬虫


13..抽象类和接口

(1)抽象类:
abstract
抽象类和常规类很像,但是不能使用new 操作符创建它的实例。
一个包含抽象方法的类必须声明为抽象类。
抽象类的构造方法定义为protected, 因为它只被子类使用。
抽象方法是非静态的。
抽象类是不能使用new 操作符来初始化的。但是,仍然可以定义它的构造方法,这个构造方法在它的子类的构造方法中调用。
子类可以覆盖父类的方法并将它定义为abstract。这是很少见的,但是它在当父类的方法实现在子类中变得无效时是很有用的。在这种情况下,子类必须定义为abstract。
即使子类的父类是具体的,这个子类也可以是抽象的。
(2)示例学习:抽象类Number
Number 类是教值包装类、Biglnteger 以及BigDecimal 的抽象父类。

Number 定义为数值类的父类,这样可以定义方法来执行数值的共同操作。
(如果doubleValue() 方法没有在Number类中定义,将不能使用Number 类从各种不同类型的数值中找到最大数值。)
(3)示例学习:Calender和GregorianCalender
GregorianCalendar 是抽象类Calendar 的一个具体子类。
java.util.Date:表示以毫秒为精度的特定时刻。
java.util.Calender:抽象基类,日历信息。如:GregorianCalender公历类
P432:类图


(4)接口:
接口是一种与类相似的结构,只包含常量和抽象方法。
(共同行为:例如,使用正确的接口,可以指明这些对象是可比较的、可食用的,以及可克隆的)
interface implements(instanceof)
在Java 中,接口被看作是一种特殊的类。
可以使用接口作为引用变量的数据类型或类型转换的结果等。
类和接口之间的关系称为接口继承(interface inheritance)。因为接口继承和类继承本质上是相同的,所以我们将它们都简称为继承。

一个接口可以继承一个或多个接口。
(5)Comparable接口
可比较的 compareTo方法,用于比较对象

packet java.lang;
public interface Comparable<E>{
public int compareTo(E o);
}
Java 类库中的许多类实现了Comparable 接口以定义对象的自然顺序。Byte、Short、Integer、Long、Float、Double、Character、Biglnteger、BigDecimal、 Calendar、String以及Date 类都实现了Comparable 接口。因此,数字是可比较的,字符串是可比较的,日期也是如此。

如果对象是Comparable接口类型的实例的话,Java API 中的jaya.util.Arrays.sort( Object[] )方法就可以使用compareTo 方法来对数组中的对象进行比较和排序。(不能使用sort方法来对一个Rectangle 对象数组进行排序,因为Rectangle 类没有实现接口Comparable)

P368 P440:强烈建议(尽管不要求)compareTo 应该与equals 保持一致。也就是说,对于两个对象o1和o2, 应该确保当且仅当o1.equals(o2)为true 时o1.compareTo(o2)==0 成立。
(6)Cloneable接口:
可克隆的对象 对象拷贝 clone方法
package java.lang;
public interface Cloneable{
}
这个接口是空的。一个带空体的接口称为标记接口(marker interface)。一个标记接口既不包括常量也不包括方法。它用来表示一个类拥有某些特定的属性
实现Cloneable 接口的类标记为可克隆的,而且它的对象可以使用在Object 类中定义的clone() 方法克隆

Java 库中的很多类(例如,Date、Calendar 和ArrayList ) 实现Cloneable。这样,这些类的实例可以被克隆。
例:Calender c2=(Calender)c.clone(); //c2和c是内容相同的不同对象

可以使用clone方法克隆一个数组,
例:int[] list1={1,2}; int[] list2=list1.clone();

为了定义一个自定义类来实现Cloneable 接口,这个类必须覆盖Object 类中的clone()方法。

House 类实现在Object 类中定义的clone 方法,方法头:protected native Object clone() throws CloneNotSupportedException;
关键字native 表明这个方法不是用Java 写的,但它是JVM 针对自身平台实现的。
关键字protected 限定方法只能在同一个包内或在其子类中访问。由于这个原因,House 类必须覆盖该方法并将它的可见性修饰符改为public, 这样,该方法就可以在任何一个包中使用。
因为Object 类中针对自身平台实现的clone方法完成了克隆对象的任务,所以,在House 类中的clone 方法只要简单调用super .clone() 即可。在Object 类中定义的clone 方法会抛出CloneNotSupportedException 异常。


深复制、浅复制:


(7)接口与抽象类:
一个类可以实现多个接口,但是只能继承一个父类。

Java 只允许为类的扩展做单一继承,但是允许使用接口做多重扩展。
public class NewClass extends BaseClass implements Interface1, ... ,InterfaceN{
……
}

利用关键字extends, 接口可以继承其他接口。这样的接口称为子接口。
public interfaceNewInterfaceextendsInterface1, ... ,InterfaceN{
……
}

所有的类共享同一个根类Object , 但是接口没有共同的根。
与类相似,接口也可以定义一种类型。一个接口类型的变量可以引用任何实现该接口的类的实例。

通常,推荐使用接口而非抽象类,因为接口可以定义非相关类共有的父类型。接口比类更加灵活。

(8)
注意,当使用加号(+) 将一个字符串和一个对象进行链接时,使用的是来自toString() 方法的这个对象的字符串表示同这个字符串进行链接。因此,r1+ "+" + r2+ "=" +r1.add(r2)等价于r1.toString()+"+"+r2 .toString()+"="+rl.add(r2).toString() 。
String 类和基本类型值的包装类也都是不可变的。

(9)类的设计原则:
内聚性:
一致性:一般来说,应该具有一致性地提供一个公共无参构造方法,用于构建默认实例。如果一个类不支持无参的构造方法,要用文档写出原因。
如果不想让用户创建类的对象,可以在类中声明一个私有的构造方法,Math 类就是如此。
封装性:
清晰性:
完整性:
实例和静态:不要从构造方法中传人参数来初始化静态数据域。最好使用set 方法改变静态数据域
继承和聚合:
抽象类和接口:接口比抽象类更加灵活,因为一个子类只能继承一个父类,但是却可以实现任意个数的接口。
然而,接口不能具有具体的方法。可以结合接口和抽象类的优点,创建一个接口,使用一个抽象类来实现它。可以视其方便使用接口或者抽象类。