温习java反射,让你对反射有全新的认识

2021/11/26 22:10:47

本文主要是介绍温习java反射,让你对反射有全新的认识,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在正常的类操作过程中,一定要先确定使用的类,再利用关键字new 产生实例化对象后使用。 但是在反射里面,可以通过Object 类中的getClass() ==(public final Class<?> getClass() )==方法实现.

1.Class 类对象实例化

第一种:调用 Object 类中的 getClass() 方法,要使用此类必须有实例化对象

mport java.util.Date;
public class Person {
    public static void main(String[] args) {
        Date date = new Date();
        Class<?> clazz = date.getClass();
        System.out.println(date.getClass());
        System.out.println(clazz.getName());
    }
}
运行结果:
class java.util.Date
java.util.Date

第二种:使用“类.class” 取得,此时不许用通过指定的实例化对象获取。

public class Person {
    public static void main(String[] args) {

        Class<?> clazz = java.util.Date.class;
        System.out.println(clazz.getName());

    }
}
运行结果:
java.util.Date

第三种:调用 Class 类提供的方法:public static Class<?> forName(String className)Throws ClassNotFoundException

public class Person {
    public static void main(String[] args) throws Exception {
	//此方法只需要定义一个具体的名称就可以取得反射操作类对象,此类需要确实存在不存在会抛出异常
        Class<?> clazz = Class.forName("java.util.Date");
        System.out.println(clazz.getName());
    }
}
  • 利用getClass() 方法操作一般出现简单的 Java 类与提交参数的自动赋值操作中,像Struts、Spring MVC 都会提供表单参数与简单的 Java 类的自动转换。

  • 利用类.class 的方式往往 是将反射操作的类型设置交由用户使用,像 Hibernate 中进行数据保存以及根据ID 查询中会使用到此类操作;

  • 利用Class.forName() 方法可以实现配置文件以及 Annotation 配置的反射操作 ,几乎所有的开发框架都是依靠此方式实现的。

2.反射实例化对象

接下来就可以利用Class 类来进行类的反射控制。在Class 类中提供有一些常用的方法,自己百度查看(常用 10个)
有一点需要注意的是,如果利用 Class 类中 的 newInstance() 方法反射实例化类对象,则类中一定要提供无参构造方法,否则会出现语法错误。也可以进一步深入利用反射调用指定的无参构造。

package com.cj;

/*
* 利用反射实例化对象
* */

//创建一个Book 书类
class Book{
//    Book的无参构造方法
    public Book(){
        System.out.println("Book类的无参构造方法");
    }

//  @Override  它说明了被标注的方法重载了父类的方法,起到了断言的作用,这里没有重载父类
    @Override
    public String toString() {
        return "java 开发反射实例化对象学习";
    }
}

public class Person {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.cj.Book");   //设置操作对象类的名称
//        反射实例化后的对象都是Object 类型
        Object obj = clazz.newInstance();   //相当于使用new 调用无参构造方法
        Book book = (Book) obj ;
        System.out.println(book);
    }
}
运行结果:
Book类的无参构造方法
java 开发反射实例化对象学习

该代码没有通过关键字new 对Book进行实例化操作 ,而是通过Class类定义的操作类名称,并利用 newInstance() 方法进行对象实例化,此时会默认调用类中的无参构造。但是该方法返回的Object 类型,需要向下转型 ,转换为子类实例。

java 这样做有什么意义?

答:利用反射机制实例化对象可以实现 更好的解耦合操作。

利用反射实现工厂设计模式

package com.cj;

/*
* 利用反射实例化对象
* */

interface Fruit{
    // 动作
    public void eat();
}
class Apple implements Fruit{
    @Override
    public void eat(){
        System.out.println("eat 苹果");
    }
}
class Orange implements Fruit{
    @Override
    public void eat(){
        System.out.println("eat 橘子");
    }
}
class Factory {
    public static Fruit getInstance(String className) {
        Fruit fruit = null;
        try {   //反射实例化,子类对象可以使用Fruit 接收
            fruit = (Fruit) Class.forName(className).newInstance();
        } catch (Exception e) {
            //没有处理异常
        }
        return fruit;
    }
}

public class Person {
    public static void main(String[] args) {
        //直接传递类名称
        Fruit fa = Factory.getInstance("com.cj.Apple");
        Fruit fb = Factory.getInstance("com.cj.Orange");
        fa.eat();
        fb.eat();
    }
}
运行结果:
eat 苹果
eat 橘子

在实际的开发中将以上的工厂设计模式再结合一些配置文件,列如(XML格式文件),就可以利用配置文件来动态定义项目中所需要的操作类,此时的程序将变得非常灵活。

3.使用反射调用构造

使用newInstance() 方法实现反射实例化对象有限制,就是需要提供无参构造。所以当类中只有有参构造的时候,就必须通过java.lang.reflect.Constructor 类来实现对象的反射实例化操作。

虽然有解决方案,但每一个类的构造方法中的参数数量可能不一样多,此时如果需要编写一个公共的反射实例化对象的工具类会比较麻烦。

取得类中的构造方法
取得全部构造方法:public Constructor<?> [] getConstructors() throws SecurityException
取得指定参数类型的构造方法:public Constructor<T> getConstructor(Class<?>...parameterTypes) throws NoSuchMethodException,SecurityException

Constructor 类的常用操作方法
描述(return):方法名
返回构筑方法上所有抛出异常的类型(Class<?>[]):getExceptionTypes()
取得构造方法的修饰符(int):getModifiers()
取得构造方法的名字(String):getName()
取得构造方法中的参数个数(int):getParameterCount()
取得构造方法中的参数类型Class<?>[]):getParameterTypes()
调用指定参数的构造实例化类对象(T):newInstance(Object ...initargs) 此方法使用了可变参数化

修饰符利用数字描述,所有的修饰符本质上都是数字加法操作。假如(public=1,final=16),那么(public final)=(1+16)17,在java.lang.reflect.Modifer 类中明确的定义了各个修饰符对应的常量操作。
数字转修饰符的方法:public static String toString (int mod)

package com.cj;


import java.lang.reflect.Constructor;

class Book{
    private String title;
    private double price;
    public Book(String title,double price){
        this.title = title;
        this.price = price ;
    }
    public String toString(){
        return "图书名称:" + this.title +","+"图书价格:"+this.price ;
    }
}


public class Person {
    public static void main(String[] args) throws Exception{    //此类需要存在,所以需要抛出异常
        Class<?> clazz = Class.forName("com.cj.Book");
//        明确找到Book类中的两个参数的构造,第一个是String ,第二个是double
        Constructor<?> constructor = clazz.getConstructor(String.class ,double.class);
        Object object = constructor.newInstance("java实战",11.2);
        System.out.println(object);
    }
}
运行结果:
图书名称:java实战,图书价格:11.2
4.反射调用方法

利用反射机制实现类方法的操作,可以利用Class类 完成
Class 类取得普通方法的操作
取得全部方法(Method[]):getMethods()
取得指定类中指定方法名称与参数类型的方法(Method):getMethod(String name,Class<?>...parameterTypes)

Method 类的常用方法
取得方法修饰符(int):getModifiers()
getReturnType()
getParameterCount()
getParameterTypes()
反射调用方法并且传递执行方法所需要的参数数据:invoke(Object object ,Object...args)
getExceptionTypes()

invoke() 反射调用的核心操作。

package com.cj;


import java.lang.reflect.Method;
import java.util.*;
class Book{
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}


public class Person {
//    首字母大写第一种方法
    public static String initcap(String string){
        char[] chars = string.toCharArray();
        if(chars[0]>='a' && chars[0]<='z'){
            chars[0] = (char) (chars[0]-32);
        }
        return new String(chars);
    }
//    第二种方法
    public static String initcaps(String string){
    //substring 取子串
        return string.substring(0,1).toUpperCase() +string.substring(1);
    }
    public static void main(String[] args)throws Exception {
        String fieldName = "title";  //要操作成员的名称
        Class<?> clazz = Class.forName("com.cj.Book");  //取得要操作的反射对象
        Object object = clazz.newInstance();    //实例化对象
//        取得类中的setTitle方法,由于title的首字母需要大写,所以调用init处理,参数类型为String
        Method setMed = clazz.getMethod("set" + initcap(fieldName),String.class);
//        取得类中的getTitle方法,笨方法不接受参数并且没有返回值类型声明
        Method getMed = clazz.getMethod("get"+initcap(fieldName));
        setMed.invoke(object,"java开发");
        System.out.println("书名:"+getMed.invoke(object));

    }
}

运行结果:
书名:java开发

当取得了对应方法的Method 对象后就可以利用 invoke() 指定方法所在类的实例化对象以及相应的参数实现方法的反射调用。

5.反射调用成员

成员的取得依然需要通过Class 类方法。

Class 类中取得成员的操作
取得本类定义的全部成员(Field[]):getDeclaredFields()
取得本类指定名称的成员(Field):getDeclaredField(String name)
取得本类继承父类的全部成员(Field[]):getFields()
取得本类继承父类中指定名称的成员(Field):getField(String name)

返回值类型都是java.lang.reflect.Field,此类可以描述类成员的信息。在Field类中也定义了一些方法。

Field 类的常用方法
getType()
get(Object obj)
set(Object obj,Object value)
还有许多setXxx()getXxx() ,自行查看官方文档。

package com.cj;


import java.lang.reflect.Field;

class Book{
    private String title;
    //私有属性并没有定义setter,getter方法
}


public class Person {

    public static void main(String[] args)throws Exception {
        Class<?> clazz = Class.forName("com.cj.Book");  //取得反射对象
        Object object = clazz.newInstance();    //给出实例化对象
        Field titleField = clazz.getDeclaredField("title"); //取得类中的title属性;
        titleField.setAccessible(true); //取消封装
        titleField.set(object,"java开发");	//相当于:this.title = "java开发"
        System.out.println(titleField.get(object));	//相当于:this.title
    }
}
运行结果:
java开发

注意封装操作的解除,如果不使用setAccessible(true) 会报错,虽然这样做了可以直接进行成员的访问,但是这样的代码严格意义上不标准,所有的属性的访问还是通过setter、getter方法操作。


如何确定当前的软件项目运行的语言环境;实现多语言切换,必须针对每一个语言提供一个资源文件,并且可以根据语言环境选择不同的资源文件进行信息的读取。

6.使用Locale类定义语言环境

如果要对用户的语言环境进行定义,则可以使用 java.util.Locale 类完成。

Locale 类常用的方法
设置要使用的语言以及国家编码:public Locale(String language,String country) 构造
取得当前语言环境的 Locale 类对象:public static Locale getDefault()

package com.cj;
import java.util.Locale;

public class Person {
    public static void main(String[] args)throws Exception {
        Locale locale = Locale.getDefault();  //取得本地默认的Local对象
        System.out.println(locale);

    }
}
运行结果:
zh_CN
//zh表示使用的语言是中文,CN表示的是国家是中国
6.利用 ResourceBundle 读取资源文件

资源文件一般都是以“key,value” 的形式保存文本信息,这样读取信息的时候就可以根据指定的 key 取得对应的 value 数据,资源文件的名称是有要求的,必须以,“*.properties" 作为文件的后缀。 可以利用 java.util.ResourceBundle 类完成。

ResourceBundle 类的常用方法
根据当前默认语言环境,取得资源对象(ResourceBundle ):getBundle()
根据指定的语言环境,取得资源对象(ResourceBundle ):getBundle(String baseName,Locale locale)
根据 key 取得对应的 value 数据(String):getString(Sgring key)

package com.cj;

import java.util.ResourceBundle;
//info = www.cj.com
public class Person {
    public static void main(String[] args)throws Exception {
//        需要自己定义一个Message.properties 文件
        //此时的Message.properties 一定要放在CLASSPATH路径下
        ResourceBundle resourceBundle = ResourceBundle.getBundle("com.cj.Message");
        System.out.println(resourceBundle.getString("info"));
    }
}

运行结果:
www.cj.com

其实可以通告占位符在资源文件中采用动态的内容设置,而这就必须已考 java.text.MessageFormat 完成。设置占位符的方法为:public static String format(String pattern,Object...arguments)

package com.cj;

import java.text.MessageFormat;
import java.util.ResourceBundle;
/*
* info = 更多内容访问:{0},讲师:{1}
* info = \sss\ssss\s\s\s\s{0}\s\s{1}
* */
public class Person {

    public static void main(String[] args)throws Exception {
//        需要自己定义一个Message.properties 文件
        //此时的Message.properties 一定要放在CLASSPATH路径下
        ResourceBundle resourceBundle = ResourceBundle.getBundle("com.cj.Message");
        System.out.println(MessageFormat.format(resourceBundle.getString("info"),"www.cj.com","铿"));
    }
}





这篇关于温习java反射,让你对反射有全新的认识的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程