继承与反射笔记

继承
extend关键字 可以在父类的基础上增加属于自己的变量实例和方法
当父类方法在子类中需要修改时,可以覆盖该方法,要注意的是子类不能直接访问父类的私有实例变量,可以使用super关键字调用父类方法
(不同于this引用,super不是对象的引用,而是绕过动态查找方法并调用特定方法的指令)
当创建一个新方法与父类方法重名时,可以用 @override注解标注要覆盖的父类方法
父类赋值,java虚拟机会启用动态方法查找,在子类上也可以访问父类方法。
在java中,父类赋值同样适用于数组,但也可能出现错误
Manager[] bosses = new Manager[];
Employee[] empls = bosses; //合法
empls[0] = new Employee(); //运行时会产生错误
因为empls和bosses引用相同的Manager[]数组,他不能持有更上层的父类Employee的对象,运行时抛出ArrayStoreException异常
在接口中,可以使用instanceof操作符,让父类引用转化为子类引用
if(empl instanceof Manager){
Manager m = (Manager)empl;
m.setbonus(1000);//该方法在Manager子类中才有
}
值得注意的,当一个方法声明为final时,子类不可以覆盖它
例如Object的getClass方法是final的,即不允许对象隐藏自己所属的真实类,
同时也可以阻止其他人继承自己的类,可以用到final修饰,比如String ,LocalTime , URL
抽象方法和类,一个类没有定义实现方法,并且强迫子类去实现它,用abstract修饰符标记
不同于接口,抽象类可以拥有实例变量和构造函数
不可以直接构造抽象类的实例,但是可以将一个具体子类对象赋给抽象类对象
受保护访问 protected 限制方法只能被子类访问,或者允许子类访问父类的实例变量
在java中, protected 赋予了包级别的访问,保护了来自其他包的访问
匿名子类(不推荐),举例 new ArrayList<String>(){{add("aa");add("bb");}} // 外面的大括号是创建匿名子类,里面的是初始化模块
继承和默认方法,当某个类继承另一个类以及实现一个接口时,发现被继承类和被实现接口有同名方法,则父类的实现总是“赢过”接口实现,所以子类不需要解决这个冲突,而两个接口实现,则必须解决冲突。“类比接口优先”原则上保证了与java7的兼容性。
带super的方法表达式
public static class Worker{
public void work(){
for(int i=0;i<100;i++)System.out.println("working");
}
}
public static class ConcurrentWorker extends Worker{
public void work(){
Thread t = new Thread(super::work);
t.start();
}
}
super关键字调用父类的work方法

Object:终极父类
java中所有的类都直接或者间接地继承自Object类,当没有显示地声明一个类的父类时,它会隐式地继承Object
Object.toString() 产生一个表示该对象的字符串,默认该字符串是类名称和哈希码
Object.equals(Object other) 两对象是同一个时返回true,其他情况均返回false。值得注意的是可以采用线程安全的Object.equals(obj,other),取代obj.equals(other)
Object.hashCode() 产生该对象的哈希码
Class<?> getclass() 产生描述该对象所属类的Class对象
protected Object clone() 拷贝该对象,浅拷贝
protected void finalize() 当垃圾回收期回收该对象时,此方法会被调用,注意不要覆盖该方法
1.toString()
当一个对象和一个字符串连接时,java编译器自动调用该对象的toString方法。而且不用写成x.toString().而是直接写“”+x.这样在x为null或者基本类型时,表达式也可以正常工作
对于数组的toString(),输出的字符串包含数组的类型[I@1540e19d中“[I”前缀表示是一个整数数组。一般用Arrays.toString(primes)方法打印数组。Arrays.deepToString(primes)打印多维数组
2.equals方法
认为两个相等的对象是完全相同的,而且检测耗费很小
与null比较时,所有的equals方法都要返回false
重载Object类的equals方法,而Object类equals方法的参数也是Object类型,因此在转换类型前,需要使用getClass方法,或者instanceof操作进行类型检查
最后比较实例变量,对于基本类型使用==操作符。对于double类型值,如果担心正无穷或者NaN,那么使用Double.equals方法。对于对象使用Object.equals(x,y),当x为null时返回false。如果是数组,则使用Array.equals来检查数组是否具有相等的长度,以及相应的数组元素是否相等。
子类中定义equals方法时,要先调用父类的equals方法,如果父类的检查没有通过,那么对象肯定不相等。父类的实例变量相等时,再调用子类的实例变量。
3.hashCode方法
哈希码是一个整数,来源于对象。哈希码应该是杂乱无序的,如果x,y是两个不相等的对象,那么x.hashCode()与y.hashCode()很有可能不同。
String类的计算hashCode()方法.
int hash;
for(int i=0;i<length();i++)
hash = hash*31+charAt(i);
hashCode与equals方法兼容,当x.equals(y)时,x.hashCode == y.hashCode
当重新定义equals方法时,也需要重新定义hashCode方法,以兼容equals方法,如果不这样做,当用户将你的类插入HashMap时,它们可能会丢失。
4.clone
最需要注意的是clone方法时浅拷贝,简单的从原对象中拷贝所有实例变量到被拷贝对象里
如果要复写clone接口需要实现 Cloneable接口

枚举
public enum Size {SMALL,MEDIUM,LARGE,EXTRA_LARGE};
与toString方法相反的操作是静态方法valueOf(字符串转其他类型),例如 Size notMySize = Size.valueOf("SMALL");
表示将notMySize赋给Size.SMALL。如果给定名称的实例不存在,valueOf会抛出异常。
values()函数会返回一个按照其声明次序排列的包含所有枚举实例的数组。for(Size s:Size.values())System.out.println(s);
枚举类型的构造函数、方法和域
public enum Size{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
private String abbr;
Size(String abbr){ this.abbr = abbr; }//枚举类型的构造函数总是私有的
public String getAbbr() { return abbr; }
};

运行时类型信息和资源
1.Class类
Object o = new Object();
Class<?> c = o.getClass(); //<?>不可省略
System.out.println(c.getName());//获取到class的名字
Class.forName("java.util.Scanner");用于构造那些可能在编译时还不被知晓的类的Class对象。
值得注意的是数组对象的getName()方法会返回额外信息"[L java.lang.String",推荐使用getSimpleName或是getTypeName
2.资源加载
InputStream inputStream = main.class.getResourceAsStream("text.txt");//资源文件和类文件在同一位置
Scanner s = new Scanner(inputStream);
System.out.println(s.nextLine());
3.类加载器
在执行java程序时,会涉及三个类加载器
bootstrap类加载器加载Java类库,是虚拟机的一部分
扩展类加载器从jre/lib/ext目录中加载"标准库扩展"部分
系统类加载器加载应用程序类。他定位classpath中目录和JAR文件的类
注意,没与bootstrap类加载器对应的ClassLoader对象。例如String.Class.getClassLoader()会返回null,而扩展类和系统加载器都是在Java中是实现的,是URLClassLoader类的实例。
值得注意的是,ClassLoader.loadClass方法不会执行静态初始化代码块,需要用Class.forName(className,true,loader)中第二个参数true确保静态初始化模块得以执行。
4.双亲委派模式
顶部bootStrap类加载器 -> 扩展类加载器 ->系统类加载器 ->自定义类加载器
子类加载器收到加载类请求时,抛给父类加载器,并且只有当父类加载器不能完成加载时,才返回给自类加载器进行加载。
优点:确立了优先级,防止核心类被替换

反射
java.lang.reflect包有三个类,Field类,Method类,Contructor类分别描述一个类的域,方法,构造函数。这三个类都有一个getName方法,该方法返回成员名称
举例获取一个类的所有方法
public static void getClassMethod(String className)throws ClassNotFoundException{//抛出未找到相关类给上层
Class<?> c = Class.forName(className);//获取到该类
while (c!=null){
for(Method method:c.getDeclaredMethods()){//迭代显示所有被定义的该类方法
System.out.println(
Modifier.toString(method.getModifiers())+" "+//Modifier.toString可以将method.getModifiers()得到的int型转化为字符串为public、private等
method.getReturnType().getCanonicalName()+" "+//得到返回值类型
method.getName()+Arrays.toString(method.getParameters())//得到方法名字、方法参数
);
}
c = c.getSuperclass();//返回父类型
}
}
举例获取一个对象的域值
public static void getObjectField(Object object) throws IllegalAccessException{
for(Field f:object.getClass().getDeclaredFields()){
f.setAccessible(true);//在使用Field和Method对象前,需要让其可访问,默认java的jvm在没有安全管理器下运行,
// 并且setAccessible方法解锁了域,但是安全管理器可以阻塞请求,并以这种方式保护对象不被访问。
Object value = f.get(object);
System.out.println(f.getName()+":"+value);
}
}
举例通过反射机制改变对象的某个阈值,在没有相关修改方法的时候比较实用
try {
Field field =student.getClass().getDeclaredField("age");
field.setAccessible(true);
int age = 20;
field.setInt(student,age);
}catch (Exception e){
e.printStackTrace();
}
使用数组
public static Object[] badCopyOf(Object[] arrays,int length){
Object[] newArrays = new Object[length];
for(int i=0;i<arrays.length;i++){
newArrays[i] = arrays[i];
}
return newArrays;
}
copy = (Student[])badCopyOf(students,10);//非法,因为badcopyof返回值是Object[],并非Student[],无法进行强制类型转换。

public static Object goodCopyOf(Object arrays ,int length){//使用反射机制扩展数组
Class<?> c = arrays.getClass();//得到对象类型
if(!c.isArray()){return null;}//判断是否为数组
Class<?> c_component_type = c.getComponentType();//得到数组元素类型
int c_component_length = Array.getLength(arrays);//得到数组元素数量
Object newArray = Array.newInstance(c_component_type,length);//创建新数组
for(int i=0;i<c_component_length;i++){
Array.set(newArray,i,Array.get(arrays,i));//复制原数组内容到新数组
}
return newArray;
}
copy = (Student[])goodCopyOf(students,10);//因为goodcopyof返回的是对象,不是对象数组,故可以进行强制类型转换。

代理
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
java.lang.reflect.Proxy类
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
可以参考看看这个:http://blog.csdn.net/pangqiandou/article/details/52964066

 

posted on 2018-04-14 16:32  LeaveMeAlone1995  阅读(187)  评论(0编辑  收藏  举报