自己学习时记的笔记整理
//反射后可以做的操作
@Test
public void test2() throws Exception{
//1. 通过反射,创建Person类对象
Class clazz = Person.class;
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom",12);
Person p = (Person) obj;
System.out.println(p);
//2. 通过反射,调用对象指定的属性、方法
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p);
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
//3. 通过反射,可以调用Person类的私有结构。私有的构造器、属性、方法
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");
System.out.println(p1);
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"Han");
System.out.println(p1);
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
showNation.invoke(p1,"中国");
// String nation = (String)showNation.invoke(p1,"中国");//相当于返回一个String
// System.out.println(nation);
System.out.println(nation);
}
Tip1:开发中尽量用直接new的方式调用公共结构【编译时就可以检查】
当不能确定要创建什么类的对象时选择反射的方式
Tip2:反射和封装性不矛盾【封装性中的private表明不建议外部使用,但是非要用通过反射调用也行】
java.lang.Class类
- 类的加载过程
程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾),接着使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,即为运行时类,就作为Class的一个实例。 - Class的实例就对应着一个运行时类。
- 加载到内存中的运行时类,会缓存一段时间。在此时间内,可以通过不同方法获取此运行时类。
获取Class实例☆
方式一:调用运行时类的属性:.class
Class<Person> clazz1 = Person.class;//Class的泛型可加可不加
System.out.println(clazz1);
方式二:通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
方式三:调用Class的静态方法:forName(String classPath)【常用】
【classPath要写出能唯一标识指定类的位置】
Class clazz3 = Class.forName("p1.Person");
System.out.println(clazz3);
方式四:使用类的加载器:ClassLoader(了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("p1.Person");
System.out.println(clazz4);
可以有Class对象的类型:
class:外部类,成员(成员内部类、静态内部类),局部内部类,匿名内部类
interface
数组:只要数组的元素类型和维度相同,就是同一个class(int [10]和int[100])
enum
annotation
基本数据类型
void
ClassLoader
对于自定义类,使用系统类加载器进行加载
调用系统类加载器的getParent():获取扩展类加载器
调用扩展类加载器的getParent():无法获得引导类加载器【引导类加载器主要负责加载java的核心类库,无法加载自定义类】
通过ClassLoader获取配置文件(.properties)信息
public void test2() throws IOException {
Properties pros = new Properties();
//方式一:此时文件路径默认识别在当前module下
FileInputStream fis = new FileInputStream("jdbc.properties");
pros.load(fis);
//方式二:使用ClassLoader,文件路径默认识别在当前module的src下
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println(user+password);
}
创建运行时类的对象☆
通过反射创建对应的运行时类的对象
newInstance():调用此方法,创建对应的运行时类的对象。实际内部调用了运行时类的空参构造器
要求:
- 运行时类必须提供空参的构造器
- 空参构造器的访问权限得够。通常设置为public
Class<Person> clazz = Person.class;
Person p = clazz.newInstance();
System.out.println(p);
获取运行时类的完整结构(了解)
getModifiers():权限修饰符(输出012形式,可加上.toString()输出public,private…)
getType():数据类型
getName():变量名、方法名
属性
getFields():获取当前运行时类及其所有父类中声明为public的属性
getDeclaredFields():获取当前运行时类中声明的所有属性【所有权限的】
方法
getMethods():获取当前运行时类及其所有父类中声明为public的方法
getDeclaredMethods():获取当前运行时类中声明的所有方法【所有权限的】
getAnnotations():获取方法声明的注解(返回一个数组)
getReturnType():返回值类型
getParameterType():形参列表类型(返回数组)【数组[i].getName()获取形参名】
getExceptionTypes():抛出的异常(返回数组)【数组[i].getName()获取异常名】
构造器
getConstructors():获取当前运行时类中声明为public的构造器
getDeclaredConstructors():获取当前运行时类中声明的所有构造器
父类
getSuperClass():获取父类
getGenericSuperClass():带泛型的父类
getActualTypeArguments():父类的泛型(返回数组)【(ParameterizedType)带泛型的父类的对象.该方法】
接口
getInterfaces():获取接口(返回数组)
getPacket():获取所在的包
getAnnotations():获取声明的注解
调用运行时类的指定结构☆
创建运行时类的对象
不常用↓
获取指定的属性:要求运行时类中属性声明为public
Field id = clazz.getField("id");
set(参数1,参数2):参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
id.set(p,10011);
get(参数1):参数1:获取哪个对象的当前属性值
int pId = (int) id.get(p);
常用方法↓
Field id = clazz.getDeclaredField("id");
id.setAccessible(true);
setAccessible():设置对获取的属性的操作权限【不设置的话private的属性不能操作】
获取指定方法同理
方法名.invoke(参数1,参数2):参数1:方法的调用者 参数2:给方法形参赋值的实参
Method show = clazz.getDeclaredMethod("show",String.class);
show.setAccessible(true);
show.invoke(p,"CHN");
获取静态方法
//private static void showDesc()
Method showDes = clazz.getDeclaredMethod("showDesc");
showDes.setAccessible(true);
Object returnVal = showDes.invoke(null); //方法没有返回值则传一个null
Object returnVal = showDes.invoke(Person.class);//两种参数都可以
获取指定构造器
getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Person per = (Person) constructor.newInstance("Tim");
动态代理
静态代理:代理类和被代理类在编译期间,就确定下来了
interface ClothFactory{
void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂1");
factory.produceCloth();
System.out.println("代理工厂2");
}
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Nike");
}
}
public class StaticProxtTest {
public static void main(String[] args) {
//被代理类
NikeClothFactory nike = new NikeClothFactory();
//代理类
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
proxyClothFactory.produceCloth();
}
}
实现动态代理需要解决的问题:
Q1:如果根据加载到内存中的被代理类,动态地创建一个代理类及其对象
Q2:当通过代理累的对象调用方法a时,如何动态地去调用被代理类中的同名方法a
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelief() {
return "fly";
}
@Override
public void eat(String food) {
System.out.println("吃"+food);
}
}
//代理类
class ProxyFactory{
//调用此方法,返回一个代理类的对象。解决问题一
public static Object getProxyInstance(Object obj){
MyInvocationHandler handler = new MyInvocationHandler();
handler.setObj(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
public void setObj(Object obj) {
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动调用如下的方法:invoke()
//将被代理类要执行的方法a的功能就声明在invoke中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnVal = method.invoke(obj,args);
//上述方法的返回值就作为当前类中的invoke()的返回值
return returnVal;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance:代理类的对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//不能写成是SuperMan的对象,否则就成自己代理自己了。
// 默认是Object,可以写成是他们的共同接口
//当通过代理类对象调用方法时,会自动调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("cake");
System.out.println("**************************************");
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
proxyClothFactory.produceCloth();
}
}