Inheritance 继承
- 继承
- 强制类型转换
- 抽象类
- Object类
- 对象包装器与自动装箱
- 枚举
- 反射
关键字 extends
表示继承,表明正在构造的新类派生于一个已存在的类
已存在的类称为:
超类(superclass)、基类(basicclass)、父类(parent class)
新类称为:
子类(subclass)、派生类(derivedclass)、孩子类(child class)
在Java中,所有的继承都是公有继承,而没有C++里的私有继承和保护继承
在通过扩展超类定义子类的时候,仅需要指出子类与超类的不同之处。
**因此在设计类的时候,应该将通用的方法放在超类,而将具有特殊用途的方法放在子类中。这种将通用功能放到超类的做法,在面向对象程序设计中十分普遍。**
在子类中可以增加域、增加方法或者覆盖超类的方法,然而绝对不能删
除继承的任何域和方法。
强制类型转换
只能在继承层次内进行类型转换
在将超类转换成子类之前,应该使用
instanceof
进行检查Manager boss = (Manager) staff[1];
等价于
if( staff[1] instanceof Manager){ Manager boss= (Manager) staff[1]; }
在一般情况下,应该尽量少用类型转换和instanceof
运算符
抽象类
包含一个或多个抽象方法的类本身必须被声明为抽象的
除了抽象方法之外,抽象类还可以包含具体数据和具体方法
类即使不含抽象方法,也可以将类声明为抽象类
Object类
Object类是Java中所有类的超类。如果没有明确地指出超类,Object就被认为是这个类的超类。
可以用Object类型的变量引用任何类型的对象
Object obj = new Employee("Peter", 2000);
想要对其中的内容进行具体的操作,还需要清楚对象的原始类型,并进行相应的类型转换:
Employee e = (Employee)obj;
在Java中,只有基本类型(primitive types)不是对象。例如,数值、字符和布尔类型的值都不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展于Object类。
equals方法
Object类中的equals
方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。
**如果对比的两个参数都为null,Object.equals(a,b)的调用将返回true。**
hashCode方法
散列码(hash code)是由对象导出的一个整型值。散列码是没有规律的。
由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的储存地址。
public int hashCode(){
return Object.hashCode(name , salary ,hireday);
}
Equals与hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。
toString方法
如果x是任意一个对象,并调用
System.out.println(x);
println方法就会直接调用x.toString(),并打印输出得到的字符串。
Object类定义了toString方法,用来打印输出对象所属的类名和散列码(十六进制),也可以通过
x.getClass().getName()+x.hashCode()
//注意这里得到的x.hashCode()为十进制
对象包装器与自动装箱
- 对象包装器
有时候需要将int这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类。例如,Integer
类对象基本类型int
。通常,这些类称为包装器(wrapper)
这些对象包装器类分为:
Integer
、Long
、Float
、Double
、Short
、Byte
、Character
、Void
、Boolean
(前6个类派生于公共的超类Number
)
对象包装器是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器还是final
,因此不能定义它们的子类
假设想定义一个整型数组列表。而尖括号中的类型参数不允许是基本类型,也就是说,不允许写成`ArrayList<int>`。这里就用到了`Integer`对象包装器类。我们可以声明一个`Integer`对象的数组列表
ArrayList<Integer> list = new ArrayList();
!警告:由于每个值分别包装在对象中,所有
ArrayList<Integer>
的效率远远低于int[]
数组。因此,应该用于小型集合,其原因是此时程序员操作的方便性要比执行效率更加重要
- 自动装箱
下面这个调用:
list.add(3);
将自动变换成:
list.add(Integer.valueOf(3));
这种变换被称为自动装箱(autoboxing)
相反的,当将一个Integer
对象赋给一个int
值时,将会自动地拆箱。
int n = list.get(i);
被编译器翻译成:
int n = list.get(i).intValue();
自动装箱规范要求
boolean
、byte
、char
<=127、介于-128~127的short
和int
被包装到固定的对象中
装箱和拆箱是编译器认可的,而不是虚拟机。
枚举类
定义枚举类
public enum Size{
SMALL , MEDIUM , LARGE , EXTRA_LARGE
};
其中toString
方法能够返回枚举常量名
System.out.println(Size.SMALL.toString());
> SMALL
toString
的逆方法是静态方法valueOf
Size s = Enum.valueOf(Size.class , "SMALL");
将s
设置成Size.SMALL
每个枚举类型都有一个静态的values
方法,它将返回一个包含全部枚举值的数组
Size[] x = Size.values();
返回包含元素Size.SMALL
、Size.MEDIUM
、Size.LARGE
、Size.EXTRA_LARGE
的数组
反射
能够分析类能力的程序成为反射(reflective)。
反射机制的功能极为强大,其可以用来:
- 在运行中分析类的能力
- 在运行中查看对象,例如,编写一个
toString
方法供所有类使用 - 实现通用的数组操作代码
- 利用
Method
对象,这个对象很像C++中的函数指针
Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被成为运行时的类型标识,这个信息跟踪着每个对象所属的类。
保存这些信息的类被称为Class
类。Object
类中的getClass()
方法会返回一个Class
类型的实例
Employee e;
Class cl = e.getClass();
一个Class对象将表示一个特定类的属性。最常用的Class
方法是getName()
。这个方法会返回类的名字
System.out.prinltln(e.getClass().getName());
> Employee
如果一个类在一个包里,包的名字也作为类名的一部分
import java.util.*;
Date d = new Date();
Class cl = d.getClass();
System.out.println(cl.getName());
> java.util.Date
还可以调用静态方法获得类名对应的Class对象
String className = "java.util.Date";
Class cl = Class.forName(className);
Class.forName()返回值为一个类,其作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
如果类名保存在字符串中,并可以在运行中改变,就可以使用这个方法。当然,这个方法只有在
className
是类名或接口名时才能够执行。否则,forName
方法将抛出一个checkedException
。无论何时使用这个方法,都应该提供一个异常处理器(exception handler)
如果T
是任意的Java类型,T.class
将代表匹配的类对象
Class cl1 = Date.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
注意:一个
Class
对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如,int
不是类,但int.Class
是一个Class
类型的参数!警告:由于历史原因,
getName
在应用于数组类型是会返回一个奇怪的值
System.out.println(Double[].class.getName()); >[Ljava.lang.Double System.out.println(int[].class.getName()); >[L
- newInstance()方法
newInstance()
方法可以快速创建一个类的实例
e.getClass().newInstance();
和关键字new
相比,前者使用类加载机制,后者是创建一个新类。
从JVM的角度看,我们使用关键字
new
创建一个类的时候,这个类可以没有被加载。但是使用newInstance()
方法的时候,就必须保证:1、这个类已经加载;
2、这个类已经连接了
java工厂模式中经常使用newInstance()
创建对象,例如
String className = Example;
Class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();
其中ExampleInterface
是Example
的接口
现在可以看出,newInstance()
实际上是把new
这个方式分解为两步,即首先调用Class
加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class
的静态加载方法forName
时获得更好的灵活性,提供给了一种降耦的手段。
分析类的能力
在java.lang.reflect
包中有三个类,Field
、Method
和Constructor
分别用于描述类的域、方法和构造器,其都有getName
方法
Field
类有一个getType
方法,用来返回描述域所属类型的Class
对象
Method
和Constructor
类有能够报告参数类型的方法
这三个类还有一个叫做getModifiers
的方法,它将返回一个整型数值,用不同的位开关描述public
和static
这样的修饰符使用状况
可以利用java.lang.reflect
包中的Modifier
类的静态方法分析getModifiters
返回的整型数值。
例如:
可以使用`Modifier`类中的`isPublic`、`isPrivate`或`isFinal`判断方法或构造器是否是`public`、`private`或`final`。我们需要做的全部工作就是调用`Modifier`类的相应方法,并对返回的整型数值进行分析,另外,还可以利用`Modifier.toString`方法将修饰符打印出来。
例子:
import java.util.*;
import java.lang.reflect.*;
public class Hello{
public static void main(String[]args){
//read class name from command line args or user input
String name;
if(args.length>0){
name = args[0];
}else{
System.out.println("please input a class name : ");
Scanner in = new Scanner(System.in);
name = in.next();
}
try{
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if(modifiers.length()>0){
System.out.println("modifiers: "+modifiers+" ");
}
System.out.println("class "+name);
if(supercl!=null&&supercl!=Object.class){
System.out.println("superClass :"+supercl.getName());
}
System.out.println();
printConstructors(cl);
System.out.println();
System.out.println();
printMethods(cl);
System.out.println();
System.out.println();
printFields(cl);
System.out.println();
System.out.println();
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
/**
* print all constructors of a class
* @param cl a class
*/
public static void printConstructors(Class cl){
System.out.println("constructors");
Constructor[] constructors = cl.getDeclaredConstructors();
for(Constructor c :constructors){
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if(modifiers.length()>0){
System.out.println("modifiers: "+modifiers+" ");
}
System.out.print(name + " ( ");
//print parameter types
Class[] paramTypes = c.getParameterTypes();
for(int j = 0;j<paramTypes.length;j++){
if(j>0){
System.out.print(" , ");
}
System.out.print(paramTypes[j].getName());
}
System.out.print(" ) ");
}
}
/**
* print all methods of a class
* @param cl a class
*/
public static void printMethods(Class cl){
System.out.println("Methods");
Method[] methods = cl.getDeclaredMethods();
for(Method m : methods){
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
//print modifiers , return type and method name
String modifiers = Modifier.toString(m.getModifiers());
if(modifiers.length()>0){
System.out.println("modifiers: "+modifiers+" ");
}
System.out.print(retType.getName()+" "+name + " ( ");
//print parameter types
Class[] paramTypes = m.getParameterTypes();
for(int j = 0;j<paramTypes.length;j++){
if(j>0){
System.out.print(" , ");
}
System.out.print(paramTypes[j].getName());
}
System.out.print(" ) ");
}
}
/**
* print all fields of a class
* @param cl a class
*/
public static void printFields(Class cl){
System.out.println("Fields");
Field[] fields = cl.getDeclaredFields();
for(Field f:fields){
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if(modifiers.length()>0){
System.out.println("modifiers: "+modifiers+" ");
}
System.out.print(type.getName()+" "+name + " ; ");
}
}
}
当输入java.lang.Double
时,console输出:
modifiers: public final
class java.lang.Double
superClass :java.lang.Number
constructors
modifiers: public
java.lang.Double ( double ) ~ modifiers: public
java.lang.Double ( java.lang.String )
Methods
modifiers: public
boolean equals ( java.lang.Object ) modifiers: public static
java.lang.String toString ( double ) modifiers: public
java.lang.String toString ( ) modifiers: public
int hashCode ( ) modifiers: public static
int hashCode ( double ) modifiers: public static
double min ( double , double ) modifiers: public static
double max ( double , double ) modifiers: public static native
long doubleToRawLongBits ( double ) modifiers: public static
long doubleToLongBits ( double ) modifiers: public static native
double longBitsToDouble ( long ) modifiers: public volatile
int compareTo ( java.lang.Object ) modifiers: public
int compareTo ( java.lang.Double ) modifiers: public
byte byteValue ( ) modifiers: public
short shortValue ( ) modifiers: public
int intValue ( ) modifiers: public
long longValue ( ) modifiers: public
float floatValue ( ) modifiers: public
double doubleValue ( ) modifiers: public static
java.lang.Double valueOf ( java.lang.String ) modifiers: public static
java.lang.Double valueOf ( double ) modifiers: public static
java.lang.String toHexString ( double ) modifiers: public static
int compare ( double , double ) modifiers: public static
boolean isNaN ( double ) modifiers: public
boolean isNaN ( ) modifiers: public static
boolean isInfinite ( double ) modifiers: public
boolean isInfinite ( ) modifiers: public static
boolean isFinite ( double ) modifiers: public static
double sum ( double , double ) modifiers: public static
double parseDouble ( java.lang.String )
Fields
modifiers: public static final
double POSITIVE_INFINITY ; modifiers: public static final
double NEGATIVE_INFINITY ; modifiers: public static final
double NaN ; modifiers: public static final
double MAX_VALUE ; modifiers: public static final
double MIN_NORMAL ; modifiers: public static final
double MIN_VALUE ; modifiers: public static final
int MAX_EXPONENT ; modifiers: public static final
int MIN_EXPONENT ; modifiers: public static final
int SIZE ; modifiers: public static final
int BYTES ; modifiers: public static final
java.lang.Class TYPE ; modifiers: private final
double value ; modifiers: private static final
long serialVersionUID ;