基本思想:基于已有的类创建新的类。
增加新的方法和字段,使新类适应新的情况
原则上最一般的方法放在超类,特殊方法放在子类
子类中提供一个新的方法覆盖超类中的方法
子类访问超类中的方法,使用super关键字
子类构造器不能访问超类中的私有字段,用一个构造器初始化私有字段,利用super语法调用构造器,必须是子类构造器第一条内容
class Manager{ super.(name,age,salary); }
this的含义: 1. 指示隐式参数的引用 2. 调用该类中的其他构造器
由一个公共超类派生出所有类的集合称为继承层次
多态:一个对象变量可以指示多种实际类型的现象
动态绑定:运行时自动选择适当的方法
子类每个对象也是超类的对象,is-a指的是程序中出现超类对象的任何地方都可以用子类对象替代
不能把超类的引用赋给子类变量
子类 对象 = 超类 (×)
子类引用数组可以转换为超类引用的数组
对于private方法,static方法,final方法,构造器,编译器可以准确知道应该调用哪个方法
调用方法过程:
1. 虚拟机获取e的实际类型方法表 2. 虚拟机查找有该方法的类 3. 虚拟机调用这个方法
我们希望阻止人们利用某个类定义子类,很简单,把他变成final类
某个类的对象引用转换或另外一个类的对象引用
进行强制类型转换,才能通过运行时的检查
一个良好的程序设计习惯:在进行强制类型转换之前,查看是否能够成功的转换
Manager a = new Manager("a", 1, 1, 1, 1); Employee b = new Employee("b", 2, 2, 2, 2); if (a instanceof Employee){ System.out.println("√"); }}
祖先类拥有一般性,将它作为派生其他类的基类
把类中的字段标记为private ,方法标记为public
需求:限制超类中的某个方法只允许子类访问,希望允许子类的方法访问超类的某个字段
protected 保护字段
private | 仅对本类可见 |
---|---|
public | 对外部完全可见 |
protected | 对本包和所有子类可见 |
默认 | 对本包可见 |
Object 类型变量只能用于作为各种值的一个泛型容器
清楚对象的原始类型,进行相应的强制类型转换
equals方法用于检测一个对象是否等于另外一个对象
equals方法确定两个对象引用是否相等,如果两个对象引用相等,两个对象肯定就相等
只有两个对象属于同一个类时,才可能相等
编写一个完美的equals方法:
显式参数命名为otherObject,稍后强转为一个名为other的变量
检测this和otherObject是否相等
检测otherObject是否为null,如果是返回false
比较this与otherObject的类
将otherObject强转为相应类型变量
根据相等性概念的要求比较字段,用==比较基本类型字段,用equals比较对象字段,如果所有字段都匹配,返回true,否则 false
子类重新定义equals,其中包含一个super.equals(other)调用
public boolean equals(Object otherObject){ if (this == otherObject){ return true; } if (otherObject == null){ return false; } if (getClass() != otherObject.getClass()){ return false; } Employee other = (Employee)otherObject; return Objects.equals(name,other.name) &&salary == other.salary && Objects.equals(hireDay,other.hireDay); }
散列码是由对象导出的一个整型值,每个对象都有一个默认的散列码,其值由对象存储地址得出,通过hashCode码判断地址是否相同
返回表示对象值的一个字符串,可以由子类调用,随处可见
toString 方法是一种非常有用的调试工具
强烈建议自定义的每一个类添加toString方法,自己和他人都收益
class EqualsTest{ public static void main(String[] args) { Employee a_a1 = new Employee("A A", 75000, 1987, 12, 15); Employee a_a2 = a_a1; Employee a_a3 = new Employee("A_A", 75000, 1987, 12, 15); Employee b_b = new Employee("B_B", 50000, 1989, 10, 1); System.out.println("a_a1 == a_a2" + (a_a1 == a_a2)); System.out.println("a_a1 == a_a3" + (a_a1 == a_a3)); System.out.println("a_a1.equals(a_a3)" + (a_a1.equals(a_a3))); System.out.println("a_a1.equals(b_b)" + (a_a1.equals(b_b))); Manager c_c = new Manager("C_C", 80000, 1987, 12, 15); Manager boss = new Manager("C_C", 80000, 1987, 12, 15); boss.setBonus(5000); System.out.println("boss.toString():" + boss); System.out.println("c_c.equals(boss):" + c_c.equals(boss)); System.out.println("a_a1.hashCode()" + a_a1.hashCode()); System.out.println("a_a3.hashCode()" + a_a3.hashCode()); System.out.println("b_b.hashCode()" + b_b.hashCode()); System.out.println("c_c.hashCode()" + c_c.hashCode()); } } class Employee{ private String name; private double salary; private LocalDate hireDay; public Employee(String name, double salary,int year,int month,int day) { this.name = name; this.salary = salary; hireDay = LocalDate.of(year,month,day); } public String getName() { return name; } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent){ double raise = salary * byPercent / 100; } public boolean equals(Object otherObject){ if (this == otherObject){ return true; } if (otherObject == null){ return false; } if (getClass() != otherObject.getClass()){ return false; } Employee other = (Employee)otherObject; return Objects.equals(name,other.name) &&salary == other.salary && Objects.equals(hireDay,other.hireDay); } public int hashCode(){ return Objects.hash(name,salary,hireDay); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", salary=" + salary + ", hireDay=" + hireDay + '}'; } } class Manager1 extends Employee{ private double bonus; public Manager1(String name, double salary, int year, int month, int day) { super(name, salary, year, month, day); bonus = 0; } @Override public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } public void setBonus(double bonus){ this.bonus = bonus; } @Override public boolean equals(Object otherObject) { if (!super.equals(otherObject))return false; Manager1 other = (Manager1) otherObject; return bonus == other.bonus; } @Override public int hashCode() { return Objects.hash(super.hashCode(), bonus); } @Override public String toString() { return super.toString() + "[bonus=" + bonus + "]"; } }
java 允许运行时确定数组的大小
Arraylist类,自动调整数组容量,不需要为此编写代码
弥补数组短板
访问或改变数组元素用get set 方法,而不是较简单的下标
既可以灵活扩展数组,又可以方便访问数组下标
ArrayList<Employee> employees = new ArrayList<>(); employees.add(new Employee("d",3,3,3,3)); employees.add(new Employee("d",3,3,3,3)); employees.add(new Employee("d",3,3,3,3)); employees.add(new Employee("d",3,3,3,3)); Employee[] employees1 = new Employee[employees.size()]; employees.toArray(employees1);
public void update(ArrayList list){} public ArrayList find(String query){} ArrayList<Employee> result = e.find(query);//报错 ArrayList<Employee> result = (ArrayList<Employee>) e.find(query);//还是报错 @SuppressWarnings("unchecked") ArrayList<Employee> result = (ArrayList<Employee>) e.find(query);//√ //确保代码没问题,再写注解
public void print(int a,Object ... args){}
构造器私有
public static void main(String[] args) { Num[] values = Num.values(); for (Num value : values) { System.out.println(value); } } } enum Num{ PI(3.14),E(2.7); private double d; Num(double d) { this.d = d; } public double getD() { return d; } public void setD(double d) { this.d = d; } @Override public String toString() { return "Num{" + "d=" + d + '}'; } }
反射是 一种功能强大且复杂的机制,适合开发工具的程序员,不适合一般的应用程序员
反射库提供了丰富且精巧的工具集,用来编写能够操纵java代码的程序,使用反射,
java可以支持用户界面生成器,对象关系映射器,需要动态查询类能力的开发工具
能分析类能力的程序叫反射
作用:
- 在运行时分析类的能力
- 在运行时检查对象
- 实现泛型数组操作代码
- 利用Method对象,类似C++函数指针
获得class类对象的方法:
用 == 运算符实现两个类对象的比较 , instanceof 又可能是父子关系,就不可行
var className = "java.util.Random"; Class cl = Class.forName(className); Object obj = cl.getConstructor().newInstance();
运行发生错误,程序抛出一个异常,没有提供处理器,程序会终止,在控制台打印异常类型
异常分:1. 非检查型异常 2. 检查型异常
不是所有错误都能避免的,需要一个throws声明
反射机制最重要的内容—检查类的结构
publicclass java.util.Date { public java.util.Date(int, int, int, int, int, int); public java.util.Date(java.lang.String); public java.util.Date(); public java.util.Date(long); public java.util.Date(int, int, int); public java.util.Date(int, int, int, int, int); public boolean after(java.util.Date); public boolean before(java.util.Date); public boolean equals(java.lang.Object); public java.lang.String toString(); public int hashCode(); public java.lang.Object clone(); public int compareTo(java.util.Date); public volatile int compareTo(java.lang.Object); private void readObject(java.io.ObjectInputStream); private void writeObject(java.io.ObjectOutputStream); private final sun.util.calendar.BaseCalendar$Date normalize(); private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date); public static long parse(java.lang.String); public int getDate(); public static java.util.Date from(java.time.Instant); public long getTime(); public void setTime(long); public static long UTC(int, int, int, int, int, int); private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String); private final sun.util.calendar.BaseCalendar$Date getCalendarDate(); private static final sun.util.calendar.BaseCalendar getCalendarSystem(long); private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date); private static final sun.util.calendar.BaseCalendar getCalendarSystem(int); public int getDay(); public int getHours(); private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar(); static final long getMillisOf(java.util.Date); public int getMinutes(); public int getMonth(); public int getSeconds(); private final long getTimeImpl(); public int getTimezoneOffset(); public int getYear(); public void setDate(int); public void setHours(int); public void setMinutes(int); public void setMonth(int); public void setSeconds(int); public void setYear(int); public java.lang.String toGMTString(); public java.time.Instant toInstant(); public java.lang.String toLocaleString(); private static final sun.util.calendar.BaseCalendar gcal; private static sun.util.calendar.BaseCalendar jcal; private transient long fastTime; private transient sun.util.calendar.BaseCalendar$Date cdate; private static int defaultCenturyStart; private static final long serialVersionUID; private static final [Ljava.lang.String; wtb; private static final [I ttb; }
D:\Java\jdk8\bin\java.exe "-javaagent:D:\java\IntelliJ IDEA 2021.2.3\lib\idea_rt.jar=62728:D:\java\IntelliJ IDEA 2021.2.3\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk8\jre\lib\charsets.jar;D:\java\jdk8\jre\lib\deploy.jar;D:\java\jdk8\jre\lib\ext\access-bridge-64.jar;D:\java\jdk8\jre\lib\ext\cldrdata.jar;D:\java\jdk8\jre\lib\ext\dnsns.jar;D:\java\jdk8\jre\lib\ext\jaccess.jar;D:\java\jdk8\jre\lib\ext\jfxrt.jar;D:\java\jdk8\jre\lib\ext\localedata.jar;D:\java\jdk8\jre\lib\ext\nashorn.jar;D:\java\jdk8\jre\lib\ext\sunec.jar;D:\java\jdk8\jre\lib\ext\sunjce_provider.jar;D:\java\jdk8\jre\lib\ext\sunmscapi.jar;D:\java\jdk8\jre\lib\ext\sunpkcs11.jar;D:\java\jdk8\jre\lib\ext\zipfs.jar;D:\java\jdk8\jre\lib\javaws.jar;D:\java\jdk8\jre\lib\jce.jar;D:\java\jdk8\jre\lib\jfr.jar;D:\java\jdk8\jre\lib\jfxswt.jar;D:\java\jdk8\jre\lib\jsse.jar;D:\java\jdk8\jre\lib\management-agent.jar;D:\java\jdk8\jre\lib\plugin.jar;D:\java\jdk8\jre\lib\resources.jar;D:\java\jdk8\jre\lib\rt.jar;D:\java\projectfu\out\production\projectfu;C:\Users\11021\.m2\repository\junit\junit\4.13.1\junit-4.13.1.jar;C:\Users\11021\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar com.fu.javatec.chapter05.ReflectionTest Enter class name (e.g. java.util.Date): java.util.Date java.lang.Object---==== public class java.util.Date { public java.util.Date(int, int, int, int, int, int); public java.util.Date(java.lang.String); public java.util.Date(); public java.util.Date(long); public java.util.Date(int, int, int); public java.util.Date(int, int, int, int, int); public boolean after(java.util.Date); public boolean before(java.util.Date); public boolean equals(java.lang.Object); public java.lang.String toString(); public int hashCode(); public java.lang.Object clone(); public int compareTo(java.util.Date); public volatile int compareTo(java.lang.Object); private void readObject(java.io.ObjectInputStream); private void writeObject(java.io.ObjectOutputStream); private final sun.util.calendar.BaseCalendar$Date normalize(); private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date); public static long parse(java.lang.String); public int getDate(); public static java.util.Date from(java.time.Instant); public long getTime(); public void setTime(long); private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String); private final sun.util.calendar.BaseCalendar$Date getCalendarDate(); private static final sun.util.calendar.BaseCalendar getCalendarSystem(long); private static final sun.util.calendar.BaseCalendar getCalendarSystem(int); private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date); public int getDay(); public int getHours(); private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar(); static final long getMillisOf(java.util.Date); public int getMinutes(); public int getMonth(); public int getSeconds(); private final long getTimeImpl(); public int getTimezoneOffset(); public int getYear(); public void setDate(int); public void setHours(int); public void setMinutes(int); public void setMonth(int); public void setSeconds(int); public void setYear(int); public java.lang.String toGMTString(); public java.time.Instant toInstant(); public java.lang.String toLocaleString(); public static long UTC(int, int, int, int, int, int); private static final sun.util.calendar.BaseCalendar gcal; private static sun.util.calendar.BaseCalendar jcal; private transient long fastTime; private transient sun.util.calendar.BaseCalendar$Date cdate; private static int defaultCenturyStart; private static final long serialVersionUID; private static final [Ljava.lang.String; wtb; private static final [I ttb; } Process finished with exit code 0
如何查看任意对象数据字段的名字和类型
查看对象指定字段的内容,利用反射可以查看在编译时不知道的对象字段
可以获得值,可以设置值
getDeclaredFileds获得所有的数据字段,使用setAccessible便利方法将所有字段设置为可访问的
package com.fu.javatec.chapter05; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; public class Demo07 { } class ObjectAnalyzer{ private ArrayList<Object> visited = new ArrayList<>(); public String toString(Object obj) throws ReflectiveOperationException{ if (obj == null) return "null"; if (visited.contains(obj)) return "..."; visited.add(obj); Class cl = obj.getClass(); if (cl == String.class) return (String)obj; if (cl.isArray()){ String r = cl.getComponentType() + "[]{"; for (int i = 0; i < Array.getLength(obj); i++) { if (i > 0) r += ","; Object val = Array.get(obj, i); if (cl.getComponentType().isPrimitive()) r += val; else r += toString(val); } return r + "}"; } String r = cl.getName(); do { r += "l"; Field[] fields = cl.getDeclaredFields(); AccessibleObject.setAccessible(fields,true); for (Field f : fields) { if (!Modifier.isStatic(f.getModifiers())){ if (!r.endsWith("{")) r += ","; r += f.getName() + "="; Class t = f.getType(); Object val = f.get(obj); if (t.isPrimitive()) r += val; else r += toString(val); } } r += "]"; cl = cl.getSuperclass(); } while(cl != null); return r; } }
java.lang…reflect 包中的 Array 类允许动态创建数组
要获得新数组元素类型:
- 获得a数组的类对象
- 确认他是一个数组
- 使用Class类的getComponentType方法确认数组的正确类型
package com.fu.javatec.chapter05; import java.lang.reflect.Array; import java.util.Arrays; public class Demo08 { 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); } 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; } }
java没有提供途径将一个方法的存储地址传给另外一个方法
高斯林:方法指针是很危险的,而且容易出错
Field类中的get方法查看一个对象的字段,Method类有invoke方法,调用包装在当前Method对象中的方法
Object invoke(Object obj,Object... args)
第一个参数是隐式参数,其余的对象提供显式参数
对于静态方法,第一个参数可以省略,把他设置为null
打印数学函数
package com.fu.javatec.chapter05; import java.lang.reflect.Method; import java.sql.Ref; public class Demo09 { } class MethodTableTest{ public static void main(String[] args) throws ReflectiveOperationException{ Method square = MethodTableTest.class.getMethod("square", double.class); Method sqrt = Math.class.getMethod("sqrt", double.class); printTable(1,10,10,square); printTable(1,10,10,sqrt); } public static double square(double x){ return x * x; } public static void printTable(double from,double to,int n,Method f) throws ReflectiveOperationException { System.out.println(f); double dx = (to - from) / (n - 1); for (double x = from;x <= to;x += dx){ double y = (Double) f.invoke(null , x); System.out.printf("%10.4f | %10.4f%n",x,y); } } }
建议仅在绝对必要的时候才在你自己的程序中使用Method对象,通常更好的做法是使用接口以及java 8 引入的 Lambda 表达式
特别强调:建议 java 开发者不要使用回调函数的Method对象,可以使用回调的接口,这样代码执行速度块,易于维护
1. 将公共操作和字段放在超类中 2. 不要使用受保护的字段 3. 使用继承实现 is-a 关系 4. 除非所有继承的方法有意义,否则不用继承 5. 在覆盖方法时,不要改变预期的行为 6. 使用多态,而不要使用类型信息,使用多态或接口更容易维护和发展 7. 不要滥用反射