二、面向对象
参考《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);
②不可变字符串与限定字符串:
String[] t="Java#HTML#Perl".split("#");
for(int i=0;i<t.length;i++)
System.out.print(t[i]+" ");// 显示 Java HTML Perl
④依照模式匹配、替换和分隔:
"Java".matches("Java");//true
"Java".equals("Java");//true
"Java is fun".matches("Java.*");
"440-02-4534".matches("\\d{3}-\\d{2}-\\d{4}")
\\d表示单个数字位,\\d{3}表示三个数字位。
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]);
这里的正则表达式[.,:;?]指定的模式是指匹配 . 、, 、:、;或者?
⑤字符串与数组之间的转换:
char[] chars="Java".toCharArray();
String str=new String( new char[]{ 'J','a','v','a' } );
String str=String.valueOf(new char[]{ 'J','a','v','a' } );
⑥将字符和数值转换成字符串:
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类
11..继承和多态
继承
(5)Object 类及其toString()方法
多态
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){
if(o instanceof Circle)
return radius==((Circle)o).radius;
else return this==o;
}
(10)ArrayList类:java.util包
①ArrayList<String> c=new ArrayList<String>();
ArrayList<String> c=new ArrayList<>();
④注:可以使用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)防止扩展和重写:
12..异常处理和文本I/O
异常处理
try{
//……
}
catch(ArithmeticException e){
System.out.println("zero");
}
被调用的方法通常不知道在出错的情况下该做些什么,这是库方法的一般情况。 库方法可以检测出错误,但是只有调用者才知道出现错误时需要做些什么。
②通常,JavaAPI 中的每个异常类至少有两个构造方法:一个无参构造方法和一个带String 参数的构造方法。该参数称为异常消息(exceptionmessage), 它可以用getMessage()获取。
③如果在调用的方法链中找不到处理器,程序就会终止并且在控制台上打印出错信息。寻找处理器的过程称为捕获一个异常(catching an exception)。
④在catch 块中异常被指定的顺序是非常重要的。如果父类的catch 块出现在子类的catch 块之前,就会导致编译错误。
⑦从异常中获取信息:
文本I/O
Scanner input=new Scanner("13 14");//
int sum=input.nextInt()+input.nextInt();
System.out.println("Sum is"+sum); //Sum is 27