java 反射练习_JAVA反射的基础学习

反射 :reflection

程序的一种内省机制

程序可以在运行期间动态的创建对象,获取对象类型,调用对象行为

内省机制在java和.net语言中有,在早期的C,C++,delphi,vb这些语言都没有内省机制。

//方法的可变参数

public void playDisc(Class... cl) {

}

反编译后,可变参数的本质就是数组

public transient void playDisc(Class aclass[])

{

}

Class

Class类的类表示正在运行的Java应用程序中的类和接口

1 类的加载概述和加载时机

A:类的加载概述

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载连接初始化散步来实现对这个类进行初始化

加载:就是指将class文件读入内存,并为之创建一个class对象,任何类被使用时系统都会建立一个class对象。

连接:(三步:验证、准备、解析)

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

初始化 就是以前常用的初始化步骤(创建一个对象时字节码文件加载进来的过程

B加载时机

创建类的实例

访问类的静态变量,或者为静态变量赋值

调用类的静态方法

使用反射方式来强制创建某个类或接口对应的java.lang,class对象

初始化某个类的子类

直接使用java.exe命令来运行某个主类

2 类加载器的概述和分类

A类加载器的概述

负责将。class文件加载到内存中并为之生成对应的class对象。类加载机制可以不用关心,但是此处的了解有利于更好地理解程序的运行

B类加载器的分类

Bootstrap classloader 根类加载器

Extension classloader 扩展类加载器

system classloader 系统类加载器

C类加载器的作用

Bootstrap classloader 根类加载器

也被称为引导类加载器,负责Java核心类的加载

比如system,string类等,在JDK中JRE放入lib目录下rt.jar文件中

Extension classloader 扩展类加载器

负责JRE的扩展目录中jar包的加载

在JDK中的JRE的lib目录下ext目录

system classloader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

3 反射概述

A 反射概述

java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用他的任意一个方法和属性

这种动态获取的信息以及动态调用对象的方法和功能称为java语言的反射机制。

想要解剖一个类,必须先要获取到该类的字节码文件对象。

而解剖使用的就是Class类中的方法,所以要先获取到每一个字节码文件对应的class类型的对象。

B 获取对象类型的方法:(三种)

1:类名.class 静态属性class,锁对象

Class aa = Dog.class; //通过xxx.class的方式获取数据类型

2:对象.getclass(); Object类的个体class()方法,判断两个对象是否在同一个字节码文件

Dog dog = new Dog("旺财");

Class bb = dog.getClass();//在程序运行期,动态湖区

System.out.println(bb);

3:class.forname()需要抛出异常; class类中静态方法forname(),读取配置文件 可以只改配置文件获取不同的.class文件

Class.forname(“oracle.jdbc.driver.OracleDriver”);

//作用1、检查驱动是否存在

2. 通过静态代码块,给静态变量defaultDriver赋值并做了初始化操作

3. 在DriverManager.getConnection中调用前面的静态变量

Class.forName("oracle.jdbc.driver.OracleDriver");

Connection connection = DriverManager.getConnection("xxx","aa","123456");

//1. 检查数据类型是否存在

2. 通过静态代码块给静态变量初始化 -----静态变量存储与JVM的方法区

3. ClassLoader ,把数据类型加载到JVM的方法区

4. 返回Class对象

C 三种获取对象类型的方式实例

public class Test1_reflect { //三种获取class的方式实例

/**

*

* @param args

* @throws ClassNotFoundException

*/

public static void main(String[] args) throws ClassNotFoundException {

Class clazz1= Class.forName("bean.Person");

Class clazz2= Person.class;

Person p=new Person();

Class clazz3=p.getClass();

System.out.println(clazz1==clazz2);

System.out.println(clazz2==clazz3);

}

}

4 Class,forname()读取配置文件举例

榨汁机:

public class Test2_reflect {

/*

* 用forname获取对象,在使用时只需要更改配置文件即可以完成操作

*/

public static void main(String[] args) throws Exception {

/*没用到反射

* Juicer j=new Juicer(); //购买榨汁机

* j.run(new Apple()); //榨汁机中放苹果

* j.run(new Orange()); //榨汁机中放橘子*/

//用反射和配置文件

BufferedReader br=new BufferedReader(new FileReader("config.properties"));

Class clazz =Class.forName(br.readLine());

Fruit f= (Fruit) clazz.newInstance(); //父类引用指向子类对象,水果的引用指向了苹果对象

//clazz.newInstance();根据配置文件创建了一个Object类型的对象,使用时需要强转

Juicer j=new Juicer();

j.run(f);

}

}

interface Fruit{

public void squeeze();

}

class Apple implements Fruit{

public void squeeze() {

System.out.println("榨出一杯苹果汁");

}

}

class Orange implements Fruit{

public void squeeze() {

System.out.println("榨出一杯橘子汁");

}

}

class Juicer{

public void run(Fruit f) {

f.squeeze();

}

/*public void run(Apple a) {

a.squeeze();

}

public void run(Orange o) {

o.squeeze();

}*/

}

5 通过反射获取带参构造方法并使用

constructor

Class类的newinstance()方法时使用该类无参的构造函数和创建对象,如果一个类没有无参构造函数,就不能这样创建了,可以调用Class类的getConstructor(String。class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstan(“张三”20)方法创建对象。

public class Test3_reflect {

/*

* Class类的newinstance()方法时使用该类无参的构造函数和创建对象,如果一个类没有无参构造函数,就不能这样创建了,

* 可以调用Class类的getConstructor(String。class,int.class)方法获取一个指定的构造函数

* 然后再调用Constructor类的newInstan(“张三”20)方法创建对象。

*/

public static void main(String[] args) throws Exception {

Class clazz=Class.forName("bean.Person");

//Person p=(Person) clazz.newInstance(); 通过无参构造创建对象

//System.out.println(p);

//void.class

Constructor c=clazz.getConstructor(String.class,int.class); //反射阶段操作字节码,给的是字节码(获取有参构造)

Person p =(Person) c.newInstance("张三",23); //通过有参构造创建对象

System.out.println(p);

}

}

6 通过反射获取成员变量并使用

Field

Class.getFiled(String)方法可以获取类中指定字段(可见的),如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj,"李四")方法可以设置指定对象上该字段的值,如果是私有的需要先调用setAccessible(ture)设置访问权限,用获取的指定字段调用get(obj)可以获取指定对象中该字段的值

public class Test4_reflect {

/**

* Class.getFiled(String)方法可以获取类中指定字段(可见的),如果是私有的可以用getDeclaedField("name")方法获取,

* 通过set(obj,"李四")方法可以设置指定对象上该字段的值,如果是私有的需要先调用setAccessible(ture)设置访问权限,

* 用获取的指定字段调用get(obj)可以获取指定对象中该字段的值

* @throws Exception

*/

public static void main(String[] args) throws Exception {

Class clazz=Class.forName("bean.Person");

Constructor c=clazz.getConstructor(String.class,int.class); //反射阶段操作字节码,给的是字节码(获取有参构造)

Person p =(Person) c.newInstance("张三",23); //通过有参构造创建对象

//Field f=clazz.getField("name");//获取name字段

//f.set(p, "李四"); //修改P的姓名的值

Field f=clazz.getDeclaredField("name");//暴力反射获取字段(可以获取私有的字段)

f.setAccessible(true); //去除私有权限

f.set(p, "李四");

System.out.println(p);

}

}

7 通过反射获取方法并使用

Method

Class.getMethod(String,Class...)和Class。getDeclaredMethod(String,Class...)方法可以获取类中的指定方法,调用invoke(Object,Object...)可以调用该方法,Class。getMethod(“eat")invoke(obj)Class.getMethod("eat",int.class)invoke(obj,10)

public class Test5_Method {

/**

* Class.getMethod(String,Class...)和Class。getDeclaredMethod(String,Class...)方法可以获取类中的指定方法,

* 调用invoke(Object,Object...)可以调用该方法,

* Class。getMethod(“eat")invoke(obj)Class.getMethod("eat",int.class)invoke(obj,10)

* @param args

* @throws Exception

*/

public static void main(String[] args) throws Exception {

Class clazz=Class.forName("bean.Person");

Constructor c=clazz.getConstructor(String.class,int.class); //反射阶段操作字节码,给的是字节码(获取有参构造)

Person p =(Person) c.newInstance("张三",23); //通过有参构造创建对象

Method m=clazz.getMethod("eat"); //获取无参eat方法

m.invoke(p); //执行无参方法

Method m1=clazz.getMethod("eat",int.class); //获取有参eat方法

m1.invoke(p,10); //执行有参方法

}

}

8 通过反射越过泛型检查

A案例演示

ArrayList的一个对象,在这个集合中添加一个字符串数据。

public class Test1 {

/**

* @param args

* ArrayList的一个对象,在这个集合中添加一个字符串数据

* 泛型只在编译期有效,在运行期会被擦除掉

* @throws Exception

*/

public static void main(String[] args) throws Exception {

ArrayList list=new ArrayList<>();

list.add(111);

list.add(222);

Class clazz =Class.forName("java.util.ArrayList"); //获取字节码对象

Method m=clazz.getMethod("add", Object.class);

m.invoke(list, "abc");

System.out.println(list); //泛型被擦除掉了所添加进去的内容是新的类型

}

}

9 通过反射写一个通用的设置某个对象的某个属性为指定的值

A案例演示

public void setProperty(object obj,String propertyName,Object value){},此方法可将obj对象中名为propertyName的属性值设置为value。

public class Test2 {

/**

* public void setProperty(object obj,String propertyName,Object value){},

* 此方法可将obj对象中名为propertyName的属性值设置为value。

* @param args

* @throws Exception

*/

public static void main(String[] args) throws Exception {

Student s=new Student("张三",23);

System.out.println(s);

Tools t=new Tools();

t.setProperty(s, "name", "李四");

System.out.println(s);

}

}

class Student{

private String name;

private int age;

public Student() {

super();

}

public Student(String name,int age) {

super();

this.name=name;

this.age=age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

@Override

public String toString() {

return "Student [name=" + name + ", age=" + age + "]";

}

}

import java.lang.reflect.Field;

public class Tools {

public void setProperty(Object obj,String propertyName,Object value) throws Exception{

Class clazz =obj.getClass(); //获取字节码对象

Field f=clazz.getDeclaredField(propertyName); //暴力反射获取字段

f.setAccessible(true); //去除权限

f.set(obj, value);

}

}

练习:

已知一个类定义如下

package cn;

public class DenoCLass{

public void run(){

system.out.println("welcome to china!);

}

}

(1)写一个Properties格式配置文件,配置类的完整名称

(2)写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类,用反射的方式运行run方法。

作业:

自定义日志系统,在系统运行目录按日期建立文件夹和日志文件(Error文件单独存储)

IO流,反射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值