反射

一、反射
  动态获取信息以及动态调用对象的方法的功能。能够对任意一个类,获取类的所有属性和方法

  • 解剖类—–>获取字节码文件对象

三种获取字节码对象方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 反射联系;
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
//1. Class的方法forName
Class clazz1=Class.forName("反射联系.Main"); //需要包.类名
//2. 为类的静态方法
Class clazz2=Main.class;
//3. 创建对象调用方法getClass方法
Main m=new Main();
Class clazz3=m.getClass();
System.out.println(clazz1==clazz2);//判断三个是不是相等
System.out.println(clazz3==clazz2);
//结果都是true
}
}

  • 反射(Class.forName(“包.类名”))

榨汁机榨汁的案例
分别有 水果(Fruit) 苹果(Apple) 香蕉(Banana) 桔子(Orange) 榨汁(squeeze)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Main {
public static void main(String[] args) throws Exception {
Juicer j=new Juicer();//购买榨汁机
j.run(new Apple()); //榨汁机炸苹果
//j.run(new Orange()x);//Apple a=new Orange(); 类型不匹配
j.run(new Orange());
}
}

class Apple {
public void squeeze() {
System.out.println("榨出苹果汁儿");
}
}

class Orange {
public void squeeze() {
System.out.println("榨出橘子汁儿");
}
}
//榨汁机负责榨水果
class Juicer{
public void run(Apple a)//榨苹果
{
a.squeeze();
}

public void run(Orange o)//榨橘子
{
o.squeeze();
}
}

  但是这种问题就是如果有很多水果就不停的要建立水果类,然后不停的在Juicer类中写出榨水果的run方法比较麻烦,所以我们要考虑使用多态的方法Fruit实现squeeze()榨汁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {
public static void main(String[] args) throws Exception {
Fruit f=new Apple();//多态 -----左父右子
f.squeeze();
}
}

interface Fruit{ //实现榨汁
public void squeeze();
}


class Apple implements Fruit{//所有要用的类实现接口Fruit

public void squeeze() {
System.out.println("榨出苹果汁儿");
}
}

class Orange implements Fruit{
public void squeeze() {
System.out.println("榨出橘子汁儿");
}
}

class Juicer{
public void run(Fruit f)//run对象为接口的 f
{
f.squeeze();
}
}

而使用反射
主要是添加一个文件名为config.properties(里面写包名.你需要的实现的类名)

1
2
3
4
5
6
7
8
9
10
11
12

public class Main {
public static void main(String[] args) throws Exception {
//使用反射配置文件
BufferedReader br=new BufferedReader(new FileReader("config.properties"));//字符流
Class c=Class.forName(br.readLine()); //读取一行(\u53CD\u5C04\u8054\u7CFB.Apple)
Fruit f=(Fruit)c.newInstance(); //---父类引用指向子类对象
//建立榨汁机对象然后调用run方法run(f)
Juicer j=new Juicer();
j.run(f);//执行结果为 榨出苹果汁
}
}

  • 通过反射—>带参构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package 反射联系;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Main2 {
public static void main(String[] args) throws Exception{
Class clazz=Class.forName("反射联系.Main");//拿到字节码
//无参构造创建对象
Main m=(Main) clazz.newInstance();
System.out.println(m);

Constructor c=clazz.getConstructor(Fruit.class);//获取有参构造
Main m1=(Main) c.newInstance("Apple");//有参构造创建对象
System.out.println(m1);
}
}

  • 通过反射成员变量(私有/共有)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Constructor;
public class Main2 {
public static void main(String[] args) throws Exception{
Class clazz=Class.forName("反射联系.Main");//拿到字节码
Constructor c=clazz.getConstructor(String.class);//获取有参构造getConstructor
Main m1=(Main) c.newInstance("Apple");//有参构造创建对象

//1 共有的public
java.lang.reflect.Field f1=clazz.getField("Fruit");//获取姓名字段------getField
f1.set(m1,"Orange");//将苹果apple改成orange

//2.私有的private
java.lang.reflect.Field f2=clazz.getDeclaredField("Fruit");//暴力反射获取姓名字段-----getDeclaredField
f2.setAccessible(true); //去除私有private权限
f2.set(m1,"Orange");//将苹果apple改成orange
}
}

  • 通过反射方法(有参/无参)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Main3 {
public static void main(String[] args) throws Exception{
Class clazz=Class.forName("反射联系.Main");//拿到字节码
Constructor c=clazz.getConstructor(String.class);//获取有参构造getConstructor
Main m1=(Main) c.newInstance("Apple");//有参构造创建对象

//无参方法
Method m3=clazz.getMethod("squeeze");
m3.invoke(m3);

//有参方法
Method m2=clazz.getMethod("squeeze",int.class);
m2.invoke(m2);

}
}

二、越过泛型检查
  泛型只在编译期间有效,运行期(字节码文件)会被擦除掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Main4 {
public static void main(String[] args) throws Exception{
ArrayList<Integer> l=new ArrayList<Integer>();
l.add(111);
l.add(222);
Class c=Class.forName("java.util.ArrayList"); //拿到字节码文件
Method m=c.getMethod("add",Object.class); //更改add方法
m.invoke(l,"abc");调用Method方法的invoke修改方法添加add
System.out.println(l); //------>[111, 222, abc]
}
}

三、指定值(setProperty)
  public void setProperty(Object o,String qian,Object hou):把o对象中名字是qian的属性值—>改成hou

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
,