继承(inheritance)
继承满足“is-a”规则,即Manager is a Employee
如果子类的构造方法没有显式地调用超类的构造方法,则将自动地调用超类的无参构造,如果没有超类没有定义无参构造方法,编译报错。
this
关键字的用途:
- 引用隐式参数
- 调用该类其他的构造方法
super
关键字的用途:
- 调用超类的方法
- 调用超类的构造方法
调用其他构造方法的语句只能出现在构造方法中的第一行
import java.util.Date; import java.util.GregorianCalendar; public class ManagerTest { public static void main(String[] args) { Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000); Employee[] staff = new Employee[3]; // 这里实际上使用了多态 staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); for (Employee e : staff) { System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()); } } } class Employee { private String name; private double salary; private Date hireDay; /** * @param n the employee's name * @param s the salary * @param year the hire year * @param month the hire month * @param day the hire day */ public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } } class Manager extends Employee { private double bonus; /** * @param n the manager's name * @param s the salary * @param year the hire year * @param month the hire month * @param day the hire day */ public Manager(String n, double s, int year, int month, int day) { // 调用父类的构造方法必须出现在子类子类构造方法的第一行 super(n, s, year, month, day); bonus = 0; } @Override public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } public void setBonus(double b) { bonus = b; } } |
多态
“is-a”规则的另一种表述法是置换法则:程序中出现超类对象的任何地方都可以用子类对象置换
Java
中的对象变量都是多态的,Employee
变量既可以引用一个Employee
对象,也可以引用Employee
的任何一个子类(比如Manager
)的对象
当把子类的对象赋给父类的变量时,就发生了向上造型
// Employee是声明类型 Employee[] staff = new Employee[3]; // 等号右边是动态类型(可能是Employee对象,也可能是它的子类对象) staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); |
注意: 不能将一个超类的引用赋给子类变量(不是所有雇员都是经理)
函数调用的绑定
- 当通过对象变量调用函数的时候,调用哪个函数这件事情叫做绑定
- 静态绑定:根据变量的声明类型来决定
- 动态绑定:根据变量的动态类型来决定
java
中默认绑定都是动态绑定
final类和final方法
如果想让一个类无法被继承,可以在class
关键字前加上final
关键字,这个类的所有方法也将自动加上final
关键字
如果想让某个类的方法不能被重写,可以在方法名前加上final
关键字
将方法或类声明为final
主要目的是确保它们不会在子类中改变。
类型转换
有时候希望将超类转换为子类,这样就能调用子类的方法。但这一般是超类的设计问题。应该避免这种转换。
将超类转换为子类之前,应该使用instanceof
进行检查, 避免出现类型转换异常(ClassCastException)
Employee[] staff = new Employee[3]; // staff[0]引用的是Manager对象, staff[1]和staff[2]引用的是Employee对象 staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); // 假设要将Employee对象转换为Manager对象 Manager ma = (Manager) staff[0]; // Error, staff[1]引用的是Employee对象,会报ClassCastException异常 Manager mb = (Manager) staff[1]; // 正确做法: 转换前需要检查该对象是否属于转化后的对象类型 if (staff[1] instanceof Manager) { Manager mb = (Manager) staff[1]; } // 注意null不会抛出异常 System.out.println(null instanceof Manager); //输出false |
抽象类
抽象类天然支持多态性,因为抽象类不能实例化,只能引用非抽象子类的对象
- 抽象类不一定包含抽象方法,有抽象方法一定要定义为抽象类
- 抽象类可以包含具体数据(比如name)和具体方法(比如getName)
- 抽象类也有构造方法
- 继承抽象类的子类必须实现所有的抽象方法
- 抽象类永远不能实例化,所有只能通过子类对象调用对应实现的抽象方法。
import java.util.Date; import java.util.GregorianCalendar; public class PersonTest { public static void main(String[] args) { Person[] people = new Person[2]; // fill the people array with Student and Employee objects people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1); people[1] = new Student("Maria Morris", "computer science"); // print out names and descriptions of all Person objects for (Person p : people) System.out.println(p.getName() + ", " + p.getDescription()); } } abstract class Person { private String name; public Person(String n) { name = n; } public abstract String getDescription(); public String getName() { return name; } } class Employee extends Person { public Employee(String n, double s, int year, int month, int day) { // 调用抽象类的构造方法 super(n); salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public String getDescription() { return String.format("an employee with a salary of $%.2f", salary); } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } private double salary; private Date hireDay; } class Student extends Person { /** * @param n the student's name * @param m the student's major */ public Student(String n, String m) { // pass n to superclass constructor super(n); major = m; } public String getDescription() { return "a student majoring in " + major; } private String major; } |
Object类
所有class
默认继承自Object
除基本类型之外,所有的对象数组和基本类型数组都继承了Object
类
System.out.println(new Object() instanceof Object); // true System.out.println(new int[2] instanceof Object); // true System.out.println(new Person[2] instanceof Object); // true |
重写object.equals
package EqualsTest; import java.util.Date; import java.util.GregorianCalendar; /** * This program demonstrates the equals method. * @version 1.11 2004-02-21 * @author Cay Horstmann */ public class EqualsTest { public static void main(String[] args) { Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15); Employee alice2 = alice1; Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15); Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1); System.out.println("alice1 == alice2: " + (alice1 == alice2)); System.out.println("alice1 == alice3: " + (alice1 == alice3)); System.out.println("alice1.equals(alice3): " + alice1.equals(alice3)); System.out.println("alice1.equals(bob): " + alice1.equals(bob)); System.out.println("bob.toString(): " + bob); Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15); Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000); // System.out.println(对象),会自动调用该对象所属类的toString()方法 System.out.println("boss.toString(): " + boss); System.out.println("carl.equals(boss): " + carl.equals(boss)); System.out.println("alice1.hashCode(): " + alice1.hashCode()); System.out.println("alice3.hashCode(): " + alice3.hashCode()); System.out.println("bob.hashCode(): " + bob.hashCode()); System.out.println("carl.hashCode(): " + carl.hashCode()); } } class Employee { public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } @Override public boolean equals(Object otherObject) { // 1.检查是否引用自同一个对象(实际上就是检查两个对象的地址是否相同) // 注意我们的目的是判断两个对象的对应数据域是否相同,而不是只对比对象地址 // 每个对象对应不同的hashCode,hashCode值即该对象的地址 // 只比较相同对象的话意义不大 if (this == otherObject) return true; // 2.比较的对象为null,返回false if (otherObject == null) return false; // 3.判断是否是同一个类的对象 if (getClass() != otherObject.getClass()) return false; // 4.这里我们已经知道比较的同一个class的非空对象 Employee other = (Employee) otherObject; // 5.检查它们的数据域是否相等 // 注: 如果要检查两个数组是否相等,可以使用Arrays.equals() return name.equals(other.name) && salary == other.salary && hireDay.equals(other.hireDay); } // 如果重写了equals方法,必须重写hashCode方法 // 这样就能保证equals返回true时,两个对象的hashCode也一样 public int hashCode() { return 7 * name.hashCode() + 11 * new Double(salary).hashCode() + 13 * hireDay.hashCode(); // 组合多个散列值时,可以使用下面这种方式 // return Objects.hash(name,salary,hireDay); } public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; } private String name; private double salary; private Date hireDay; } class Manager extends Employee { public Manager(String n, double s, int year, int month, int day) { super(n, s, year, month, day); bonus = 0; } public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } public void setBonus(double b) { bonus = b; } public boolean equals(Object otherObject) { if (!super.equals(otherObject)) return false; Manager other = (Manager) otherObject; // super.equals checked that this and other belong to the same class return bonus == other.bonus; } public int hashCode() { return super.hashCode() + 17 * new Double(bonus).hashCode(); } public String toString() { return super.toString() + "[bonus=" + bonus + "]"; } private double bonus; } |
自动装箱和自动拆箱
包装器类是不可变的。
如果在一个条件表达式中混合使用Integer
和Double
类型,Integer
值就会拆箱,提升为double
, 再装箱为Double
由于包装类引用可以为null
,所以可能会抛出NullPointerException
异常
Integer i1 = 1; Double d1 = 2.0; System.out.println(true ? i1 : d1); // 输出1.0 Integer i2 = null; // error, 会抛出NullPointerException异常 System.out.println(i2 * 3); |
可变参数
找出数组中的最大值
public class Main { public static void main(String[] args) { System.out.println(max(1,2,8.8,9.9)); // 9.9 } public static double max(double... values) { // 负无穷 double largest = Double.NEGATIVE_INFINITY; for (int i = 0; i < values.length; i++) { if (values[i] > largest) largest = values[i]; } return largest; } } |
枚举类
import java.util.Scanner; public class EnumTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.print("Enter a size: (SMALL, MEDIUM, LARGE, EXTRA_LARGE) "); String input = in.next().toUpperCase(); // 创建指定名字和类的枚举常量 Size size = Enum.valueOf(Size.class, input); // toString()方法返回枚举常量名 System.out.println("size=" + size); System.out.println("abbreviation=" + size.getAbbreviation()); // 判断枚举常量时直接使用"==" if (size == Size.EXTRA_LARGE) System.out.println("Good job--you paid attention to the _."); } } enum Size { SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL"); private Size(String abbreviation) { this.abbreviation = abbreviation; } public String getAbbreviation() { return abbreviation; } private String abbreviation; } |
反射
获取class对象的三种方式:
- 使用
Object
类的getClass()
- 使用
Class
类的静态方法forName
- 直接使用
.class
jvm
为每一种类型管理一个Class
对象
// 第一种方式 Employee e = new Manager("Carl Cracker", 80000, 1987, 12, 1); System.out.println(e.getClass()); // 第二种方式(注意要捕获异常) // 动态修改要加载的类名 String className = "ManagerTest.Employee"; try { System.out.println(Class.forName(className)); } catch (ClassNotFoundException ex) { throw new RuntimeException(ex); } // 第三种方式 // Class类实际上是一种泛型类 Class<Employee> employeeClass = Employee.class; // 一个Class 对象实际上表示的是一个类型, 而这个类型未必一定是一种类 System.out.println(int.class); // 输出int |
利用反射创建一个有参构造的对象
注:new newInstance()
使用的是无参构造方法
try { Class<?> aClass = Class.forName("ManagerTest.Employee"); // 如果要使用有参构造方法,需要先获取一个构造方法 Constructor<?> c = aClass.getConstructor(String.class, double.class, int.class, int.class, int.class); // 利用上面的构造方法创建对象 Employee e = (Employee)c.newInstance("Carl Cracker", 80000, 1987, 12, 15); System.out.println(e.getName()); } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException ex) { throw new RuntimeException(ex); } |
通过反射机制获取类的成员变量,方法,构造方法等
package cc.bnblogs; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; public class Main { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<?> aClass = Class.forName("cc.bnblogs.Point"); Point p = (Point)aClass.newInstance(); // 获取类的成员变量 // 只能获取和修改public型的值 Field x = aClass.getDeclaredField("x"); Field y = aClass.getDeclaredField("y"); Field str = aClass.getDeclaredField("color"); // 要获取private变量的值,需要使用setAccessible(true) x.setAccessible(true); Object o = x.get(p); System.out.println(o); // 获取Point类的所有成员变量 Field[] fields = aClass.getDeclaredFields(); System.out.println(Arrays.toString(fields)); // error,获取对象的私有成员变量 // System.out.println(x.get(p)); System.out.println(str.get(p)); try { // 获取Point类的setColor方法 Method mSetColor = aClass.getMethod("setColor", String.class); // 修改point的颜色 mSetColor.invoke(p,"black"); System.out.println(p.getColor()); // 获取所有声明的方法 Method[] methods = aClass.getDeclaredMethods(); System.out.println(Arrays.toString(methods)); // 使用Modifier类获取权限修饰符的名称 System.out.println(Modifier.toString(aClass.getModifiers())); } catch (NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } } } class Point { private int x; private int y; public String color; Point() { } Point(int x,int y) { this.x = x; this.y = y; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } } |
反射机制实现访问父类的成员变量
Person e = new Employee("Harry Hacker", 50000, 1989, 10, 1); // 获取Employee的父类 Class<?> aClass = Class.forName("PersonTest.Employee").getSuperclass(); // 不能这样获取 // e.getClass(); 父类引用子类对象,e实际上还是Employee类 System.out.println(aClass); // 获取父类的成员变量name Field name = aClass.getDeclaredField("name"); name.setAccessible(true); Object o = name.get(e); System.out.println(o); |
利用反射创建泛型数组
java.lang.reflect
包中的Array
类允许动态地创建数组。例如, 将这个特性应用到Array
类中的copyOf
方法实现中,实现数组的动态拓展
import java.lang.reflect.Array; import java.util.Arrays; public class CopyOfTest { public static void main(String[] args) { int[] a = {1, 2, 3}; a = (int[]) goodCopyOf(a,10); System.out.println(Arrays.toString(a)); String[] b = {"Tom", "Dick", "Harry"}; b = (String[]) goodCopyOf(b,10); System.out.println(Arrays.toString(b)); System.out.println("The following call will generate an exception."); // Object不能强制转换为String // b = (String[]) badCopyOf(b,10); } public static Object[] badCopyOf(Object[] a, int newLength) { Object[] newArray = new Object[newLength]; System.arraycopy(a,0,newArray,0,Math.min(a.length,newLength)); return newArray; } public static Object goodCopyOf(Object a, int newLength) { Class cl = a.getClass(); if (!cl.isArray()) return null; Class componentType = cl.getComponentType(); int length = Array.getLength(a); Object newArray = Array.newInstance(componentType,newLength); System.arraycopy(a,0,newArray,0,Math.min(length,newLength)); return newArray; } } |
利用反射调用任意方法
- 调用静态方法,不需要获取类对象
- 调用非静态方法,需要获取类对象
- 反射调用私有静态方法(getDeclaredMethod,setAccessible)
- 反射调用私有非静态方法(getDeclaredMethod,setAccessible)
package CopyofTest; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class CopyOfTest { public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<?> aClass = Class.forName("CopyofTest.Point"); // 有参构造器 Constructor<Point> constructor = Point.class.getConstructor(int.class, int.class); Point p = constructor.newInstance(1, 2); // 调用get和set方法 Method getX = aClass.getMethod("getX"); System.out.println(getX.invoke(p)); Method setX = aClass.getMethod("setX",int.class); setX.invoke(p,10); System.out.println(getX.invoke(p)); // 调用private非静态方法 Method privateMethod = aClass.getDeclaredMethod("printStr"); privateMethod.setAccessible(true); String str = (String) privateMethod.invoke(p); System.out.println(str); // 调用private静态方法 Method privateStaticMethod = aClass.getDeclaredMethod("print"); privateStaticMethod.setAccessible(true); // 调用静态函数, 第一个对象参数设置为null privateStaticMethod.invoke(null); } } class Point { private int x; private int y; public Point(int x,int y) { this.x = x; this.y = y; } public void setX(int x) { this.x = x; } public int getX() { return x; } public void setY(int y) { this.y = y; } public int getY() { return y; } private String printStr() { return "point"; } private static void print() { System.out.println("print"); } } |