JavaSE学习总结---反射机制

概述

反射机制有什么用
通过反射机制可以操作字符码文件(就是编译后的那个class文件),在java.lang.reflect包下。
相关的类:
java.lang.class :代表字节码文件
java.lang.reflect.Method:代表字节码中的方法字节码
java.lang.reflect.Constructor:代表字节码中的构造方法字节码
java.lang.reflect.Field:代表字节码中的属性字节码

获取字节码文件的三种方式

package a.b;

public class Test01 {
    public static void main(String[] args) {
        /*
        第一种拿到字节码文件的方式
            Class.forName()
                1,静态方法
                2,方法的参数是一个字符串
                3,字符串需要是一个完整的类名,包括包的名
         */
        Class c1=null;
        try {
            c1=Class.forName("java.lang.String");//c1代表String.class文件
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        /*
        第二种拿到字节码文件的方式,java中任何一个对象都有一个方法:getClass()
            通过引用点方式获取相应的类
         */
        String s="abc";
        Class x=s.getClass();//x代表String.class字节码文件,x代表String类型
        System.out.println(c1==x);//结果为true,双等号是判断的内存地址,c1和x指向的是同一份字节码文件

        /*
        第三种拿到字节码文件的方式,java中任何一种类型,包括基本类型,它都有class属性
         */
        Class z=String.class;//z代表String类型
        System.out.println(x==z);
    }
}

通过反射实例化对象

newInstance();方法调用User类的无参构造方法,完成User对象的创建,必须保证无参构造方法存在

package a.b;

public class Test02 {
    public static void main(String[] args) {
        try {
            //通过反射机制实例化对象
            Class c=Class.forName("a.b.User");//右键User类名,点Copy Reference就可以复制带着包名了
            Object o= null;
            try {
                o = c.newInstance();//调用User类的无参构造方法,完成User对象的创建,必须保证无参构造方法存在
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            System.out.println(o);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

反射这种方式创建对象更加灵活

package a.b;

import java.io.FileReader;
import java.util.Properties;

public class Test03 {
    public static void main(String[] args)throws Exception {
        //通过IO流读取属性配置文件
        FileReader reader=new FileReader("FanSheJiZhi/classinfo.properties");
        //创建属性类对象Map
        Properties pro=new Properties();
        //加载
        pro.load(reader);//属性配置文件就加载到属性集合上了
        //关闭流
        reader.close();
        ///通过key获取value
        String s=pro.getProperty("classname");
        System.out.println(s);
        
        //通过反射机制实例化对象
        Class c= Class.forName(s);
        Object o=c.newInstance();
        System.out.println(o);


    }
}

不改变源代码的基础之上,可以做到不同对象的实例化
符合OCP开闭原则,对扩展开放,对修改关闭

forname执行静态代码块

如果只是想让一个类的静态代码块执行的话,其他代码不执行,可以Class.forname

package a.b;

public class User {
    static{
        System.out.println("执行了");
    }
}
package a.b;

public class Test {
    public static void main(String[] args) {
        try {
            Class.forName("a.b.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

关于路径问题

以上方式填写的路径,移植性差,在IDEA中默认的当前路径是project的根,如果离开了IDEA,可能当前路径不是project的根了,这个路径就无效了
以下较为通用
(使用前提是这个文件必须在类路径下,凡是在src下的都是类路径,src是类的根路径)

/*
        Thread.currentThread()当前线程对象
        getContextClassLoader()线程方法,获取当前线程的类加载器对象
        getResource类加载器的方法,类加载器默认的根路径下加载资源,起点是从类的根路径下开始加载
         */
        String path=Thread.currentThread().getContextClassLoader().getResource("文件名").getPath();
        System.out.println(path);

这种方式可以不止windows系统

String path=Thread.currentThread().getContextClassLoader().getResource("a/b/db.properties").getPath();
//该文件在src下的a包下的b包下
package a.b;

import java.io.FileReader;
import java.util.Properties;

public class Test04 {
    public static void main(String[] args) throws Exception{
        String path=Thread.currentThread().getContextClassLoader().getResource("a/b/db.properties").getPath();
        FileReader reader=new FileReader(path);

        Properties pro=new Properties();
        pro.load(reader);
        reader.close();
        
        pro.getProperty("classname");
        System.out.println(pro.getProperty("classname"));
    }
}

直接以流的方式返回
上述方式是先获取个路径,然后new个FileReader流对象,将这个流对象传到pro.load参数中,将流加载到properties集合中
下面这个方式是直接返回流,不用在获取路径了

import java.io.InputStream;
import java.util.Properties;

public class Test04 {
    public static void main(String[] args) throws Exception{
//        String path=Thread.currentThread().getContextClassLoader().getResource("a/b/db.properties").getPath();
//        FileReader reader=new FileReader(path);
        InputStream in=Thread.currentThread().getContextClassLoader().getResourceAsStream("a/b/db.properties");

        Properties pro=new Properties();
        pro.load(in);
        in.close();

        pro.getProperty("classname");
        System.out.println(pro.getProperty("classname"));
    }
}

资源绑定器

java.util包下有个资源绑定器,便于获取配置文件下的内容
必须是属性配置文件
属性配置文件xxx.properties必须在类路径下
并且写路径的时候,路径后面的扩展名不能写

package a.b;

import java.util.ResourceBundle;

public class ResourceBundleTest {
    public static void main(String[] args) {
    //getBundle("a/b/db")自动将这个路径下的文件加载到propert集合中了
        ResourceBundle bundle=ResourceBundle.getBundle("a/b/db");
        String classname=bundle.getString("classname");
        System.out.println(classname);
    }
}

类加载机制

启动类加载器
扩展类加载器
应用类加载器
双亲委派机制,一种安全机制,你自己写的一个String类,jvm加载的时候先是“启动类加载”,不会加载你写的,而是SUN公司写的
启动类加载器被称为“父”,扩展类加载器被称为“母”

获取Field(属性)

反射Field

package a.b;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FieldTest1 {
    public static void main(String[] args) throws Exception {
        //获取整个类
        Class c=Class.forName("a.b.Student");
        //获取所有的Field
        //getFields()获取所有的公开的属性
        Field[]fields= c.getFields();
//        System.out.println(fields.length);//结果为1
//        System.out.println(fields[0]);
        //getDeclaredFields()获得所有属性
        Field[]fieldss=c.getDeclaredFields();
        //System.out.println(fieldss.length);
        for (int i = 0; i <fieldss.length ; i++) {
            //获得所有属性的名字
            System.out.println(fieldss[i].getName());

            //获得属性的类型
            //System.out.println(fieldss[i].getType());//getType()返回的是个class
            //System.out.println(fieldss[i].getType().getName());//全名
            System.out.println(fieldss[i].getType().getSimpleName());

            //获得属性的修饰词
            //getModifiers()返回的是int型,Modifier类有个静态方法可以将代号转化成字符串
            //System.out.println(fieldss[i].getModifiers());
            int i1=fieldss[i].getModifiers();
            Modifier.toString(i1);
            System.out.println(Modifier.toString(i1));


        }
    }

}

class Student{
    //Field为字段就是属性
    //分别采用了不同的修饰符
    public int no;
    private String name;
    protected int age;
    boolean sex;
}

反编译Field
给一个class文件,可以拿到java源码

package a.b;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FanBanyi {
    public static void main(String[] args) throws Exception{
        //字符串拼接
        StringBuilder s=new StringBuilder();

        //获取类
        Class c=Class.forName("java.lang.Integer");

        s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{"+"\n");

        for (Field field:c.getDeclaredFields()
             ) {
            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));
            s.append(" ");
            s.append(field.getType().getSimpleName());
            s.append(" ");
            s.append(field.getName());
            s.append(";\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

通过反射机制访问对象的属性
给属性复赋值
获取属性的值

package a.b;

import java.lang.reflect.Field;

public class Test05 {
    public static void main(String[] args) throws Exception{
       Class c= Class.forName("a.b.Student");
       Object o=c.newInstance();
       //获取属性(根据属性的名称获得属性)
       Field field= c.getDeclaredField("no");

        //给o对象(student对象)的no属性赋值
        field.set(o,222);//给o对象的no属性赋值222

        //读取属性的值
        field.get(o);

        //私有修饰的属性通过上述方式修改不了
        //打破封装field1.setAccessible(true);反射机制的缺点
        Field field1=c.getDeclaredField("name");
        field1.setAccessible(true);
        field1.set(o,"jack");
        System.out.println(field1.get(o));

    }
}

有个叫打破封装的,反射机制的缺点

获取Method

可变长参数

package a.b;
/*
    可变长度参数
       类型...
       int ...args是可变长都参数
       1.可变长度是0到n个
       2.可变长参数必须在列表中的最后一个
       3.可以当一个数组来处理,这里的args就是一个数字的数组,也可以传一个数组进去
 */
public class ArgsTest {
    public static void main(String[] args) {
        m(1,2);
        m(2);
    }

    public static void  m(int...args){
        System.out.println("执行了");
    }
}

反射Method

package a.b;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test06 {
    public static void main(String[] args) throws Exception{
        //获取类
       Class c= Class.forName("a.b.UserService");

       //获取Method,所有的包括私有的
        Method [] methods=c.getDeclaredMethods();

        for (Method method:methods
             ) {
            //获取方法名
            System.out.println(method.getName());

            //获取方法返回值类型
            method.getReturnType().getSimpleName();
            System.out.println(method.getReturnType().getSimpleName());

            //获取修饰符
            int i=method.getModifiers();
            Modifier.toString(i);
            System.out.println(Modifier.toString(i));

            //获取方法的参数列表,由两部分,一是参数类型,二是参数变量名
           Class [] classes= method.getParameterTypes();
           for (Class clas:classes){
               System.out.println(clas.getSimpleName());
           }

        }
    }
}

class UserService{

    public boolean login(String name,String password){
        if("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    public void logout(){
        System.out.println("系统退出成功");
    }
}

反编译Method

package a.b;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

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

        StringBuilder s=new StringBuilder();
        Class c=Class.forName("java.lang.String");
        s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{\n");



        Method []methods=c.getDeclaredMethods();
        //public boolean login(String name,String password)
        for (Method method:methods
        ) {
            s.append("\t");
           s.append( Modifier.toString(method.getModifiers()));
           s.append(" ");
           s.append(method.getReturnType().getSimpleName());
           s.append(" ");
           s.append(method.getName());
           s.append("(");
           //参数列表
            Class []classes=method.getParameterTypes();
            for (Class cp:classes
                 ) {
                s.append(cp.getSimpleName());
                s.append(",");


            }
            s.deleteCharAt(s.length()-1);
           s.append("){}\n");

        }
        s.append("}");
        System.out.println(s);
    }
}

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

package a.b;

public class UserService1 {
    public boolean login(String name,String password){
        if("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    public void logout(){
        System.out.println("系统退出成功");
    }
}
import java.lang.reflect.Method;

public class Test07 {
    public static void main(String[] args) throws Exception{
        Class c=Class.forName("a.b.UserService");
        Object obj=c.newInstance();
        //获取方法,因为有重载,所以不能只要方法名
        Method method=c.getDeclaredMethod("login", String.class, String.class);
        //调用方法,要素1对象要素2方法名要素3参数列表要素4返回值
        //invoke调用的意思
       Object reValue= method.invoke(obj,"admin","123");
        System.out.println(reValue);
    }
}

有个小插曲,如果UserService这个类还是像之前在Test06下的时候,调用会出错

can not access a member of class a.b.UserService with modifiers 

获取Constructor

反射Constructor
反编译Constructor

package a.b;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

public class Test07 {
    public static void main(String[] args) throws Exception{
        StringBuilder s=new StringBuilder();
        Class c=Class.forName("a.b.Vips");
        s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{\n");
        Constructor []constructors=c.getConstructors();
        for (Constructor cs:constructors
             ) {
            s.append("\t");
            s.append(Modifier.toString(cs.getModifiers())+" "+c.getSimpleName());//构造方法的方法名就是类名
            s.append("(");
            Class []classes=cs.getParameterTypes();
            for (Class css:classes
                 ) {
                s.append(css.getSimpleName());
                s.append(",");
            }
            //删除最后下标位置上的字符
            //s.deleteCharAt(s.length()-1);
            if(classes.length>0){
                s.deleteCharAt(s.length()-1);
            }
            s.append(")");
            s.append("\n");
        }

        s.append("}");
        System.out.println(s);

    }
}
class Vips{
    int no;
    String name;
    String birth;
    boolean sex;

    public Vips() {
    }

    public Vips(int no, String name, String birth, boolean sex) {
        this.no = no;
        this.name = name;
        this.birth = birth;
        this.sex = sex;
    }
}

反射机制调用构造方法(new对象)

package a.b;

import java.lang.reflect.Constructor;

public class Test08 {
    public static void main(String[] args)throws Exception {
        //通过反射调用构造方法
        Class c=Class.forName("a.b.Vips");
        //无参构造
        Object o1=c.newInstance();
        //有参构造
        //第一步,获取到这个有参数的构造方法
        Constructor constructor=c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
        //调用这个方法new对象
        Object o2=constructor.newInstance(110,"jack","1990-04-21",true);
    }
}

获取父类和实现的接口

package a.b;

public class Test09 {
    public static void main(String[] args)throws Exception {
        Class c=Class.forName("java.lang.String");
        //获取父类
        String name =c.getSuperclass().getName();
        System.out.println(name);

        //获取实现的接口
        Class []classes=c.getInterfaces();
        for (Class is:classes
             ) {
            System.out.println(is.getSimpleName());
        }
    }
}

注解

Annotation
是一种引用数据类型,编译之后也生成class文件
语法格式
[修饰符列表]@interface注解类型名{ }
怎么使用?
@加上类型名

给编译器做参考的,编译阶段起作用

三个jdk自带的Annotation
@Override: 限定重写父类方法, 该注释只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告.

元注解
注解的注解叫元注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

常见的元注解Target,Retention

Target用来标注”注解类型“的”注解“,用来标注”被标注的注解“出现在哪个位置上,比如表示这个注解只可以出现在.METHOD(方法)上

Retention用来表示标注的注解存在什么地方,
.SOURCE表示只存在java源文件中
.CLASS表示保存在class文件中
.RUNTIME表示保存在class文件中,并且被反射机制读取到

注解应用
需求:
有一个注解@Id,只能出现在类上,凡是类上面出现@Id注解,要求该类必须有一个int型的id属性,如果没有就异常

自定义注解

package a.c;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//表示这个注解只能出现在类上
@Target(ElementType.TYPE)
//该注解可以被反射机制读取到
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}

自定义异常

package a.c;
/*
自定义异常
 */
public class HasnotIDException extends RuntimeException{
    public HasnotIDException() {
    }

    public HasnotIDException(String message) {
        super(message);
    }
}

自定义类,有@Id注解

package a.c;

@Id
public class User {
   int id;
}

基于注解方式编程

package a.c;

import java.lang.reflect.Field;

public class Zhujie {
    public static void main(String[] args)throws Exception {
        Class c1=Class.forName("a.c.User");
        //判断类是否有注解
        if(c1.isAnnotationPresent(Id.class)){
            //如果有注解在进行判断是否类符合注解要求
            Field [] fields=c1.getDeclaredFields();
            boolean isRight=false;//给一个默认标记
            for (Field field:fields
                 ) {
                //每次循环,拿出一个属性做判断
                if("id".equals(field.getName())&&"int".equals(field.getType().getSimpleName())){
                    //表示有个int类型的id属性
                    isRight=true;//表示合法,就不用循环了
                    break;
                }
            }

            if(!isRight){
                //判断是否合法,这如果不合法,则抛异常
                throw new HasnotIDException("该类违反了注解Id的规定");
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值