一. 反射应用
(一) 反射获取成员变量并使用
1.Class类获取成员变量对象:
Field[] getFields()
返回所有公共成员变量对象的数组
Field[] getDeclaredFields()
返回所有成员变量对象的数组
Field getField(String name)
返回单个公共成员变量对象,参数name表示成员变量的名字
Field getDeclaredField(String name)
返回单个成员变量对象,参数name表示成员变量的名字
2.Field类型: 表示一个成员变量类型,每个对象都是一个具体的成员变量
作用: 获取成员变量的各种信息(修饰符、注解、名称),做各种数据类型的转换.
3.Field类用于给成员变量赋值的方法:
set(Object obj, Object value): 用于给obj对象的,指定成员变量,赋value值
4.Field类获取成员变量值的方法:
get(Object obj): 用于获取obj对象的指定成员变量值
public class Person { private String name; private String id; int age; public String sex; public String address; public String getName() { return name; } public void setName(String name) { this.name = name; } void print(){ System.out.println("我是普通print功能,默认修饰"); } private boolean equal(double d1, double d2){ return d1 == d2; } public Person(){} public Person(String name){ System.out.println(name); } Person(String name, int age){ System.out.println(name + "--" + age); } private Person(int age){ System.out.println(age); } }
import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class TestReflect { public static void main(String[] args) throws Exception { // 1. 获取到Person类型的字节码文件对象 Class c = Person.class; // 2. 获取Person中所有的公共修饰的成员变量 Field[] fArr = c.getFields(); for(Field f : fArr){ System.out.println(f); } System.out.println("----------------------"); // 3. Field[] getDeclaredFields()返回所有成员变量对象的数组 Field[] fArr2 = c.getDeclaredFields(); for(Field f : fArr2){ System.out.println(f); } System.out.println("_______________________________"); // 4.Field getField(String name)返回单个公共成员变量对象,参数name表示成员变量的名字 Field f1 = c.getField("sex"); System.out.println(f1); // 6. 通过Field类型中的set方法给指定的成员变量进行赋值 // set(对象,变量值) Constructor con = c.getConstructor(); Person p = (Person)con.newInstance(); f1.set(p,"男"); // get(Object obj): 获取到参数obj对应的指定成员变量的值 // System.out.println(p.sex);// 男 String sex = (String)f1.get(p); System.out.println(sex);// 男 // 5. Field getDeclaredField(String name)返回单个成员变量对象,参数name表示成员变量的名字 Field f2 = c.getDeclaredField("age"); Field f3 = c.getDeclaredField("name"); System.out.println(f2); System.out.println(f3); /* f3.set(p,"张三"); System.out.println(p.getName());*/ } }
(二) 反射获取类中的成员方法并执行
1.Class类获取成员方法对象:
Method[] getMethods()
返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods()
返回所有成员方法对象的数组,不包括继承的
Method getMethod(String methodName, Class<?>...parameterTypes)
返回单个公共成员方法对象
Method getDeclaredMethod(String methodName, Class<?>...parameterTypes)
返回单个成员方法对象
2.Method类型:
(1) 表示成员方法的类型,该类型的每个对象,都是一个具体的成员方法
(2) 成员方法对象具有的功能: 获取成员方法信息,运行方法.
3.Method类用于执行方法的功能:
invoke(Object obj, Object...values):调用obj对象的成员方法,参数是values是运行方法的实际参数,返回值Object类型是方法运行的返回值结果.
public class Person { private String name; private String id; int age; public String sex; public String address; public String getName() { return name; } public void setName(String name) { this.name = name; } void print(){ System.out.println("我是普通print功能,默认修饰"); } private boolean equal(double d1, double d2){ return d1 == d2; } public Person(){} public Person(String name){ System.out.println(name); } Person(String name, int age){ System.out.println(name + "--" + age); } private Person(int age){ System.out.println(age); } }
import java.lang.reflect.Method; public class ReflectMethod { public static void main(String[] args) throws Exception{ // 1. 获取到Person类型的字节码文件对象 Class c = Class.forName("com.ujiuye.reflect.Person"); // 2. 获取Person中所有的公共修饰的方法(包括继承到的) Method[] allPubMethod = c.getMethods(); for(Method m : allPubMethod){ System.out.println(m); } System.out.println("-----------------"); // 3.Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括继承的 Method[] allMethod = c.getDeclaredMethods(); for(Method m : allMethod){ System.out.println(m); } System.out.println("--------------------------------"); // 4. Method getMethod(String methodName, Class<?>...parameterTypes)返回单个公共成员方法对象 // 第一个参数表示方法名称; 第二个参数是这个方法对应的参数列表 (因为方法可以有重载形式,因此获取某一个方法时必须说明 // 方法名称和参数列表) Method m1 = c.getMethod("setName",String.class); System.out.println(m1); // invoke(Object obj, Object...values): 第一个参数是一个对象, 第二个参数表示当前方法调用时传递的实际参数 // 注意: 如果一个类型中有公共修饰的, 空参数构造方法, Class类型中有newInstance() 快速创建出指定类型对象,调用的就是 // 这个类型的空参数构造方法 Person p1 = (Person)c.newInstance(); m1.invoke(p1,"小六六"); Method m3 = c.getMethod("getName"); System.out.println(m3); String name = (String)m3.invoke(p1); System.out.println(name); // 5. Method getDeclaredMethod(String methodName, Class<?>...parameterTypes)返回单个成员方法对象 Method m2 = c.getDeclaredMethod("equal",double.class,double.class); System.out.println(m2); boolean boo = (Boolean)m2.invoke(p1,3.14,3.14); System.out.println(boo); } }
(三) 暴力反射
1.通过Class类中:
getDeclaredXXX方法: 可以获取类中所有声明的成员(属性、方法、构造),私有的成员也可以获取到.但是私有成员进行访问使用时,会因为权限问题导致失败,因此就需要暴力反射解决访问私有的问题
2.修改该对象的访问权限:
AccessibleObject类是Field,Method和Constructor对象的基类. 它提供了将反射对象标记为在使用它时抑制默认Java语言访问控制检查的功能.
setAccessible(boolean flag): true的值表示反射对象应该在使用时抑制Java语言访问检查,false的值表示反映的对象应该强制执行Java语言访问检查.
3.一旦设定当前对象可以访问,私有的成员也可以被访问,被修改.
public class Person { private String name; private String id; int age; public String sex; public String address; public String getName() { return name; } public void setName(String name) { this.name = name; } void print(){ System.out.println("我是普通print功能,默认修饰"); } private boolean equal(double d1, double d2){ return d1 == d2; } public Person(){} public Person(String name){ System.out.println(name); } Person(String name, int age){ System.out.println(name + "--" + age); } private Person(int age){ System.out.println(age); } }
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class 暴力反射 { public static void main(String[] args) throws Exception { // 1. 获取到Person类型字节码文件对象 Class c = Person.class; // 2. 获取Person中的私有构造 Constructor privateCon = c.getDeclaredConstructor(int.class); // 将构造方法进行暴力反射 privateCon.setAccessible(true); Person p = (Person) privateCon.newInstance(18); System.out.println(p); // 3. 获取到Person中的私有成员变量 Field f = c.getDeclaredField("name"); // 将成员变量name进行暴力反射 f.setAccessible(true); f.set(p, "王五"); // 4. 获取到Person中私有方法 Method m = c.getDeclaredMethod("equal",double.class, double.class); // 将方法进行暴力反射 m.setAccessible(true); boolean boo = (Boolean)m.invoke(p,6.6,6.6); System.out.println(boo); } }
(四) 泛型擦除案例
定义出一个ArrayList<String> 集合,泛型中只能存储String类型数据, 利用反射机制,向该集合中成功添加Integer类型数据
import java.lang.reflect.Method; import java.util.ArrayList; public class 泛型擦除 { public static void main(String[] args) throws Exception{ // 定义出一个ArrayList<String> 集合,泛型中只能存储String类型数据, // 利用反射机制,向该集合中成功添加Integer类型数据 ArrayList<String> list = new ArrayList<>(); list.add("abc"); // 1. 获取到ArrayList类型字节码文件对象 Class c = Class.forName("java.util.ArrayList"); // 2. 获取到ArrayList集合中的add方法 Method m = c.getDeclaredMethod("add",Object.class); // 3. 运行m方法 m.invoke(list,12); m.invoke(list,6.89); System.out.println(list); } }
二. JDK新特性
(一) 接口的新特性
1. 概述:
jdk8之前接口是规则的集合体,方法只有抽象方法。
jdk8版本开始不光有抽象方法同时增加了实体方法。
2. 增加内容:
jdk8:default默认方法, static静态方法
jdk9: private私有方法
1.1 默认方法
1. 被关键字 default 修饰的方法就是默认方法,是在jdk8版本才出现的方法,独属于接口所有。
2. 出现的原因:在jdk8版本的时候,需要对一个接口下面的所有的实现类的功能做一个增强,就需要在接口当中去添加方法,如果接口中添加的是抽象方法,下面的实现类就需要强制去重写这些抽象方法,jdk希望在接口当中添加方法,直接就让下面的实现类去使用,不用再次的进行重写,所以添加了使用default做修饰的默认方法,默认方法是可以不被重写的,因为他有方法体。
3. 语法格式:
修饰符 default 返回值类型 方法名 (参数列表){方法体}
4. 使用规则:
(1) 加上default的,实现类可以不重写,直接调用
(2) 特殊情况1:实现类实现了两个接口,如果有相同的默认方法声明,则强制要求在实现类中,重写这个方法,以指定确定的实现内容
(3) 特殊情况2:在特殊情况1中,如果在实现类重写这个方法的过程中,希望指定其中某一个接口的默认实现,则可以调用”父接口名.super.默认方法名称(实际参数)”
(4) 特殊情况3:实现类实现了继承了一个父类,并且实现了一个接口,并且在父类中和接口中,有相同的方法声明,则“类优先”。即使继承的是一个抽象类,也是使用父类的实现(即强制重写)。
5. 产生的影响
(1) 接口中如果也可以定义非抽象方法,那么接口和抽象类的差别就越来越小
(2) java中一个类只能继承一个抽象类,但是可以同时实现多个接口,所以有了默认方法,就会让大量的抽象类变成接口,即弱化了抽象类
public interface MyInterface { // 1. JDK8之前: 接口中的方法只有抽象方法, 默认修饰符 public abstract void show(); // 2. 在JDK8版本: 可以在接口中添加默认方法default, 还有静态方法static /* 接口中添加默认方法目的: 为了后期便于接口的维护,默认方法可以直接被实现类继承使用,不强制重写 */ public default int getSum(int x, int y){ return x + y; } }
public class MyInterfaceImpl extends Fu implements MyInterface,InterfaceOther{ @Override public void show() { System.out.println("重写了父接口中的抽象方法"); } @Override public void fun() { } // 1. 如果一个类同时实现多个接口, 并且父接口中定义出相同的默认方法声明,导致实现类继承的默认方法冲突 // ,于是要求实现类必须重写重复的默认方法 /*@Override public int getSum(int x, int y){ System.out.println("我是重写的默认方法"); // 2. 如果实现类想要调用父接口中的默认方法 父接口名.super.默认方法(实际参数); return InterfaceOther.super.getSum(x,y); // return x * y; }*/ }
public class Fu { public int getSum(int x, int y){ return (x + y) * 10; } }
public interface InterfaceOther { void fun(); public default int getSum(int x, int y){ return 2 * x * y; } }
public class TestInterface { public static void main(String[] args) { // 1. 实现类可以直接继承使用从父接口中继承到的默认方法 MyInterfaceImpl mil = new MyInterfaceImpl(); mil.show(); System.out.println(mil.getSum(4,5)); } }
1.2 静态方法
1、接口的静态方法可以定义方法体内容
2、static不能和abstract共存
3、外界只能通过接口名称.静态方法来访问接口静态方法
4、实现类中不会继承接口中的静态方法,原因:如果一个类同时实现了两个接口,具有相同的静态方法名,继承之后不知道应该以哪个为准, 而静态方法又不能重写,因此矛盾无法解决
public interface MyInterface { // 3. JDK8版本中: 可以添加静态方法static // 1) 静态方法只属于接口本身, 可以用接口名.直接调用 // 2) 接口中的静态方法不给实现类继承使用 public static void print(int n){ for(int i = 1; i <= n; i++){ System.out.println(i); } } }
public class TestInterface { public static void main(String[] args) { // 2. 接口中的静态方法不能给实现类继承使用 MyInterface.print(5); //MyInterfaceImpl.print(6); } }
1.3 私有方法
1. 概述: 私有方法是jdk9版本增加的一个实体方法,主要是用来进一步封装代码,提升相关代码安全性的手段。私有化之后方法不能被实现类直接调用使用或重写修改,只能提供给接口的静态方法和默认方法使用。
2. 使用:
(1) 普通私有方法:只能提供给默认方法调用使用
(2) 静态私有方法:默认方法和静态方法都可以调用
静态方法只能使用静态的私有方法,不能使用普通的私有方法
public interface MyInterface { public default void fun(){ // fun中有很多逻辑代码,实现类就可以直接将这些代码继承,使用 // 于是为了安全度考虑, 可以将fun中的部分代码逻辑封装在一个私有方法中 test(); testShow(); } public static void show(){ testShow(); } // 1. 普通私有方法只能在当前接口中的默认方法中调用 private void test(){ System.out.println("你好"); } // 2. 静态私有方法即可以在默认方法中调用, 也可以在静态方法中进行调用 private static void testShow(){ System.out.println("我是为了静态方法代码安全性考虑设计"); } }