Java反射机制——21天学习挑战赛

本文详细探讨了Java反射机制,包括获取类对象、创建对象、访问属性、调用方法及其应用场景。通过实例展示了如何利用反射动态加载类、创建对象并修改属性值,以及调用方法。强调了反射在不修改代码情况下实现业务切换的优势。
摘要由CSDN通过智能技术生成


活动地址:CSDN21天学习挑战赛

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。

感谢CSDN举行学习挑战赛,使我成为更好的自己,按照自身的学习领域和学习进度学习并记录自己的学习过程。

创作计划

**

1,目标

通过该活动督促自己复习以前的Java内容

2,收获

通过回首复习可以更深入的了解java的语法与原理,通过多次的复习可以使自己编写代码的能力得到有效提升。提高了专业知识的同时也一定程度上提高了写作水平和表达能力,让我能够将所学的知识表达出来。也谢谢一路陪伴的朋友们和我一起加油,努力!!!

3,日常

  1. 创作是否已经是你生活的一部分了。
  2. 有限的精力下,平衡创作和工作学习使我生活更加精彩。
  3. 将所学内容消化后,再将精华“吐”出来使我交到一些好朋友。

4,憧憬

希望通过自己的努力可以不再向经济压力低头,希望自己以后的生活可以丰富多彩。

**

学习计划

**
1,学习目标

回首再望Java反射机制

2,学习内容

1.获取类对象

2.创建对象

3.访问属性

4.调用方法

5.反射有什么用

3,学习时间

8月1日晚上10点

**

学习日记(正文)

**

1.获取类对象

类对象概念: 所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法。

(1).​什么是类对象?

在理解类对象之前,先说我们熟悉的对象之间的区别:
garen和teemo都是Hero对象,他们的区别在于,各自有不同的名称,血量,伤害值。
然后说说类之间的区别
Hero和Item都是类,他们的区别在于有不同的方法,不同的属性。
类对象,就是用于描述这种类,都有什么属性,什么方法的

(2).获取类对象

获取类对象有3种方式
1. Class.forName
2. Hero.class
3. new Hero().getClass()
在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

(3).获取类对象的时候,会导致类属性被初始化

static String copyright;
static {
    System.out.println("初始化 copyright");
    copyright = "版权由Riot Games公司所有";
}

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

 如:

public class Hero {
    public String name;
    public float hp;
    public int damage;
    public int id;
 
    static String copyright;
 
    static {
        System.out.println("初始化 copyright");
        copyright = "版权由Riot Games公司所有";
    }
 
}
import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
            String className = "charactor.Hero";
            try {
                Class pClass1=Class.forName(className);
                Class pClass2=Hero.class;
                Class pClass3=new Hero().getClass();
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

2.创建对象

 与传统的通过new 来获取对象的方式不同
反射机制,会先拿到Hero的“类对象”,然后通过类对象获取“构造器对象”
再通过构造器对象创建一个对象

(1).通过反射机制创建一个对象


import java.lang.reflect.Constructor;
import charactor.Hero;
public class TestReflection {
 
    public static void main(String[] args) {
    	//传统的使用new的方式创建对象
        Hero h1 =new Hero();
        h1.name = "teemo";
        System.out.println(h1);
         
        try {
        	//使用反射的方式创建对象
            String className = "charactor.Hero";
            //类对象
            Class pClass=Class.forName(className);
            //构造器
            Constructor c= pClass.getConstructor();
            //通过构造器实例化
            Hero h2= (Hero) c.newInstance();
            h2.name="gareen";
            System.out.println(h2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

(2).通过配置文件获取对象

首先准备一个文本文件:hero.config。 在这个文件中保存类的全名称,可以是charactor.APHero 或者是charactor.ADHero
接着设计一个方法叫做:

public static Hero getHero()
public class APHero extends Hero {
  
    public void magicAttack() {
        System.out.println("进行魔法攻击");
    }
  
}
public class ADHero extends Hero {
  
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
  
}

 
import charactor.Hero;
import com.sun.org.apache.bcel.internal.util.ClassPath;
import org.junit.Test;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
 
public class How2JTest {
    /**
     * 首先准备一个文本文件:hero.config。
     * 在这个文件中保存类的全名称,可以是charactor.APHero 或者是charactor.ADHero
     * 接着设计一个方法叫做:
     * public static Hero getHero()
     * 在这个方法中,读取hero.config的数据,取出其中的类名,根据类名实例化出对象,然后返回对象。
     */
    @Test
    public void test1(){
        Hero hero = getHero();
        hero.name = "xie";
        System.out.println(hero.name);
    }
    public Hero getHero(){
        try (FileInputStream fin = new FileInputStream("hero.config")) {
            byte[] info = new byte[(int) new File("hero.config").length()];
            fin.read(info);
            String classPath = new String(info);
            System.out.println(classPath);
            Class clazz = Class.forName(classPath);
            return (Hero) clazz.newInstance();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}

3.访问属性

 通过反射机制修改对象的属性

 为了访问属性,把name修改为public。
对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。

package charactor;

public class Hero {
	public String name;
	public float hp;
	public int damage;
	public int id;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Hero(){
		
	}
	public Hero(String string) {
		name =string;
	}

	@Override
	public String toString() {
		return "Hero [name=" + name + "]";
	}
	public boolean isDead() {
		// TODO Auto-generated method stub
		return false;
	}
	public void attackHero(Hero h2) {
		System.out.println(this.name+ " 正在攻击 " + h2.getName());
	}

}

通过反射修改属性的值



import java.lang.reflect.Field;

import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
            Hero h =new Hero();
            //使用传统方式修改name的值为garen
            h.name = "garen";
            try {
            	//获取类Hero的名字叫做name的字段
                Field f1= h.getClass().getDeclaredField("name");
                //修改这个字段的值
                f1.set(h, "teemo");
                //打印被修改后的值
                System.out.println(h.name);
                
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

getField和getDeclaredField的区别:
这两个方法都是用于获取字段
getField 只能获取public的,包括从父类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。

(注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加setAccessible(true))

 4.调用方法

 通过反射机制,调用一个对象的方法

 首先为Hero的name属性,增加setter和getter通过反射机制调用Hero的setName

 


 
public class Hero {
    public String name;
    public float hp;
    public int damage;
    public int id;
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Hero(){
         
    }
    public Hero(String string) {
        name =string;
    }
 
    @Override
    public String toString() {
        return "Hero [name=" + name + "]";
    }
    public boolean isDead() {
        // TODO Auto-generated method stub
        return false;
    }
    public void attackHero(Hero h2) {
        // TODO Auto-generated method stub
         
    }
 
}

 
import java.lang.reflect.Method;
 
import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
        Hero h = new Hero();
 
        try {
            // 获取这个名字叫做setName,参数类型是String的方法
            Method m = h.getClass().getMethod("setName", String.class);
            // 对h对象,调用这个方法
            m.invoke(h, "盖伦");
            // 使用传统的方式,调用getName方法
            System.out.println(h.getName());
 
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

5.反射有什么用

 反射非常强大,但是学习了之后,会不知道该如何使用,反而觉得还不如直接调用方法来的直接和方便。
(1).首先准备两个业务类,这两个业务类很简单,就是各自都有一个业务方法,分别打印不同的字符串

package reflection;
 
public class Service1 {
 
    public void doService1(){
        System.out.println("业务方法1");
    }
}
package reflection;
 
public class Service2 {
 
    public void doService2(){
        System.out.println("业务方法2");
    }
}

(2).当需要从第一个业务方法切换到第二个业务方法的时候,使用非反射方式,必须修改代码,并且重新编译运行,才可以达到效果(非反射方式)

package reflection;
 
public class Test {
 
    public static void main(String[] args) {
        new Service1().doService1();
    }
}
package reflection;
 
public class Test {
 
    public static void main(String[] args) {
//      new Service1().doService1();
        new Service2().doService2();
    }
}

(3).使用反射方式,首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。 里面存放的是类的名称,和要调用的方法名。
在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。(反射方式)

 

class=reflection.Service1
method=doService1
package reflection;
 
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
 
public class Test {
 
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void main(String[] args) throws Exception {
 
        //从spring.txt中获取类名称和方法名称
        File springConfigFile = new File("e:\\project\\j2se\\src\\spring.txt");
        Properties springConfig= new Properties();
        springConfig.load(new FileInputStream(springConfigFile));
        String className = (String) springConfig.get("class");
        String methodName = (String) springConfig.get("method");
         
        //根据类名称获取类对象
        Class clazz = Class.forName(className);
        //根据方法名称,获取方法对象
        Method m = clazz.getMethod(methodName);
        //获取构造器
        Constructor c = clazz.getConstructor();
        //根据构造器,实例化出对象
        Object service = c.newInstance();
        //调用对象的指定方法
        m.invoke(service);
         
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无忧#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值