Java 反射,泛型,类加载

本文深入探讨了Java中的反射机制,包括如何获取和创建Class对象,使用Field和Method进行属性和方法的操作,以及动态代理的概念和实现。此外,还介绍了泛型的基本理解和应用,如集合泛型、自定义泛型以及泛型与反射的结合。通过实例展示了如何利用反射读取Excel信息和数据库参数,以及如何实现动态代理。
摘要由CSDN通过智能技术生成



前言

1、反射
使得java可以在程序运行动态期间创建类的实例,这样也扩展了后来的动态代理(基于反射实现,还有一种是基于cglib实现),动态代理和生活中的代理一个思想,红牛,他有很多委托的代理厂商去生产(大学寒暑假工代理,房产中介),肯定在每个代理中,在原有最基本的功能上,又会有一些不一样的特殊功能(价格优惠,优惠券…),每有一个新的代理,那么就会有一个新的功能(如果没有,那代理就没有太大的意义了)。而我们java程序中也是如此,模拟我们现实生活场景,在不使用反射的情况下,代码是我们提前写好了的,一旦程序运行,想要增加一个代理类,那么必须修改代码,项目重启,而且代理类可能有很多个,手写也会很麻烦。
2、类加载:类加载时静态变量赋值顺序和静态代码块的顺序(代码顺序),静态代码块不执行的情况(子类调用父类没有重写的静态方法,调用常量)
3、泛型:定义与使用,继承中的使用,通配符,泛型与反射

一、类加载

java程序运行时,首先会加载程序中所有的class文件,这个过程就是类加载,具体的流程在另外一篇中有赘述:
浅谈java类加载机制
加载类的工具是类加载:核心类加载器(rt.jar),扩展类加载器(ext.jar),系统类加载器,
加载过程:加载(创建class对象),验证(class文件中格式,),准备(分配内存),解析(符号引用转为直接引用),初始化(静态变量初始化)
类加载机制:双亲委派机制(自上而下加载,防止修改jdk中核心类)

二、反射

1、理解

1)反射:反射机制使得java可以在程序运行动态期间创建类的实例,这样也扩展了后来的动态代理(基于反射实现,还有一种是基于cglib实现),通过jdk提供的Class类,可以操控内存的字节码对象(对象是唯一的,是在程序启动时类加载过程中产生的)
2)动态代理:动态代理和生活中的代理一个思想,红牛,他有很多委托的代理厂商去生产(大学寒暑假公代理,房产中介),肯定在每个代理中,在原有最基本的功能上,又会有一些不一样的特殊功能(价格优惠,优惠券…),每有一个新的代理,那么就会有一个新的功能(如果没有,那代理就没有太大的意义了)。而我们java程序中也是如此,模拟我们现实生活场景,在不使用反射的情况下,代码是我们提前写好了的,一旦程序运行,想要增加一个代理类,那么必须修改代码,项目重启,而且代理类可能有很多个,手写也会很麻烦。

2、获取Class对象的三种方式以及创建对象

1)获取Class对象

public class GetClass {
    public static void main(String[] args) {
        Class classz = Animal.class;//和数据的length属性类似
        Class classz2 = new Animal().getClass();//对象的调用方法
        Class calssz3 = Class.forName("com.test_reflect.test_method.Animal");//class的静态方法,需要类型的全限定名称
    }
}

2)创建对象

public class test {
    public static void main(String [] args){
        try {
            Class animalClass = Class.forName("com.test_reflect.test_Constructor.Animal");
            Constructor constructor = animalClass.getDeclaredConstructor(String.class);//获取指定构造方法
            Animal animal = (Animal) constructor.newInstance("牛马");//利用Constructor类调用指定的有参构造方法创建对象
            animalClass.newInstance();//弃用,利用字节码对象利用无参数构造方法创建对象
            System.out.println(animal.getName());
        } catch (ClassNotFoundException e) {
            System.out.println(e.getMessage());
        } catch (IllegalAccessException e) {
            System.out.println(e.getMessage());
        } catch (InstantiationException e) {
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

class Animal{
    private String name;
    public Animal(){
        this("此动物名字待定");
        System.out.println("无参构造方法执行了!");
    }

    public Animal(String name){
        System.out.println("有参构造方法执行了!");
        this.name=name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3、常用方法和相关类

1)Field

①获取属性修饰符,类型,变量名
/**
 * 变量:修饰符,变量类型,变量名
 */
public class test {
    public static void main(String [] args){
        Class userClass=null;
        try {
            userClass = Class.forName("com.test_reflect.test_fields.User");
        } catch (ClassNotFoundException e) {
            System.out.println(e.getMessage());
        }
        if(userClass!=null){
            Field fields [] = userClass.getDeclaredFields();//获取所有的变量
            try {
                Object userObj = userClass.newInstance();//调用无参构造方法,创建实例
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            StringBuffer str = new StringBuffer();
            for(Field f:fields){
                str.append(Modifier.toString(f.getModifiers())+" ");//获取修饰符
                str.append(f.getType().getSimpleName()+" ");//获取变量类型,可能是
                str.append(f.getName()+";");//获取变量名
                str.append("\n");
            }
            System.out.println(str);
        }
    }
}
②操作属性,打破修饰规则,属性值得获取与修改
public class test_set_get {
    public static void main(String[] args) {
        try {
            Class userClass = Class.forName("com.test_reflect.test_fields.User");
            User user = new User();
            user.setUsername("ly");
            Object userObj = userClass.newInstance();//方法已过时;
            Field field = userClass.getDeclaredField("username");
            field.setAccessible(true);//打破规则,
            field.set(userObj , "liyang");//因为是private修饰,必须打破权限
            System.out.println(field.get(userObj));//获取属性,
        } catch (ClassNotFoundException e) {
            System.out.println(e.getMessage());
        } catch (IllegalAccessException e) {
            System.out.println(e.getMessage());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            System.out.println("没有找到属性");
        }
    }
}

2)Method

①获取方法信息

在继承关系,真正意义上的类只是继承了父类特征,调用的是父类方法,并不是自己拥有,所谓重写感觉就是在说自己拥有了父类的方法。(变量也是这个意思),
可变长度参数(就是数组,只能在形参最后,所以也只能有一个)获取的修饰符含有transient,数组不会,

public class test {
    public static void main(String[] args) {
        try {
            Class dogClass = Class.forName("com.test_reflect.test_method.Animal");
            Method [] methods=dogClass.getDeclaredMethods();//获取所有方法,不包括静态代码块
            StringBuffer str = new StringBuffer();
            dogClass.getDeclaredMethod("speak",String.class,int.class);//根据形参和方法名获取唯一的方法
            for(Method method:methods){
                str.append(Modifier.toString(method.getModifiers())+" ");//获取方法的修饰符
                str.append(method.getReturnType()+" ");//获取返回值类型
                str.append(method.getName()+"(");//获取方法名
                Parameter[] parameters = method.getParameters();//获取形参类
                for(Parameter para:parameters){
                    str.append(para.getType().getSimpleName()+",");//获取形参的类型简单名字
                }
                if(parameters.length>0)
                    str.deleteCharAt(str.length()-1);
                str.append("){}");
                str.append("\n");
            }
            System.out.println(str);
        } catch (ClassNotFoundException e) {
            System.out.println("没有加载到指定类");
        } catch (NoSuchMethodException e) {
            System.out.println("没有找到这个方法");
        }
    }
}
②方法调用

方法调用的返回值为Object,如果返回值类型为void,那么Object为null


public class test_method_doSome {
    public static void main(String[] args) {
        try {
            Class catClass = Class.forName("com.test_reflect.test_method.Animal");
            Object catObj = catClass.newInstance();
            Method method = catClass.getDeclaredMethod("speak",String.class,int.class);//获取指定method放酦
            method.invoke(catObj,"fds",432);//调用方法,方法对象,形参
        } catch (ClassNotFoundException e) {
            System.out.println("没有找到指定的类");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            System.out.println("没有找到这个方法");
        } catch (InvocationTargetException e) {
            System.out.println("执行方法时出错");
        }
    }
}

3)Interface,Annotation,SuperClass

public class getSome {
    public static void main(String[] args) {
        Class son1Class = Son1.class;
        Class superClass = son1Class.getSuperclass();//获取父类Class对象
        Class [] interfaces = son1Class.getInterfaces();//获取实现的接口Class对象
        System.out.println("superClassName:"+superClass.getSimpleName()+"/"+superClass.getName());
        for (Class anInterface : interfaces) {
            System.out.println("implementsInterface:"+anInterface.getSimpleName());
        }
    }
}

4、功能实现(反射与泛型)

1)读取excel信息

需要依赖

    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>3.15</version>
    </dependency>

处理工具类

package org.example.HandlerUtils;

import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.example.hlxyh.Column;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import static java.sql.Types.NUMERIC;
import static org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType.FORMULA;
import static org.apache.xmlbeans.impl.piccolo.xml.Piccolo.STRING;

/**
 * 转储的实体类需要加上@Column注解,没有注解标明的实体类中的实体类是不会存储的不会存储
 * sheet表格格式:第0行表示列名,第0列没有任何意义
 * bean对象属性赋值通过set方法(set+属性名首字母大写)
 */
public class HandlerUtil {
    public static <T> List<T> doSome(Class<T> classz, Sheet sheet){
        T t = null;
        Map<String,String> map = new HashMap<>();//存储列名和实体类的映射关系;
        List<T> list = new ArrayList<>();
        try {
            int rows = sheet.getPhysicalNumberOfRows();//获取总行数
            int cols = sheet.getRow(0).getPhysicalNumberOfCells();//获取第一行的列数。
            if((map = mapAdapter(classz))==null) {
                System.out.println("映射错误");
                return null;
            }

            for(int i = 1; i <rows;i++) {//遍历每一行的内容,
                Row row = sheet.getRow(i);
                t = classz.newInstance();
                for (int j = 1; j < cols; j++)//遍历每一列
                {
                    String data = row.getCell(j).toString();//获取属性

                    String field = map.get(sheet.getRow(0).getCell(j).toString());//获取赋值的属性名
                    setData(classz,t,field,data);
                }
                list.add(t);
                //存储实体类
            }
        } catch (InstantiationException e) {
            System.out.println("newInstance error");
        } catch (IllegalAccessException e) {
            System.out.println("newInstance error");
        }
        return list;
    }


    public static <T> void setData(Class<T> classz,T t,String field,String data){
        if(field==null) return;
        Method [] methods = classz.getDeclaredMethods();
        //String field =  map.get(col)!=null?map.get(col):col;
        char chars [] = field.toCharArray();
        chars[0]-=32;
        String methodName = "set"+new String(chars);
        try {
            Method method = classz.getMethod(methodName,String.class);
            method.invoke(t,data);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            System.out.println("获取属性的set方法失败,获取调用set方法失败");
        }
    }

    public static Map mapAdapter(Class classz){
        HashMap<String,String> map = new HashMap();
        Field [] fields = classz.getDeclaredFields();
        for (Field field : fields) {
            Column col = field.getAnnotation(Column.class);
            if(null!=col) map.put(col.value(),field.getName());//有注解的属性名
            //else map.put(field.getName(),field.getName());//属性名和列名相同
        }
        return map;
    }
}

@Column注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)//可存在于程序运行期间获取
public @interface Column {
    public String value();
}

实体类可自行测试

public class Bean {
    @Column("姓名")
    private String name;
    @Column("手机号")
    private String tel;
    @Column("所在学院")
    private String job;
    @Column("入学年份")
    private String time;
    @Column("现居城市")
    private String address;
}

2)(数据库信息读取,Http请求参数)数据库信息读取,Http请求参数)参数信息封装

public class BeanUtil {
    public static <T> T getBean(Class<T> classz, Map<String,Object> map){
        T obj = null;
        try {
             obj = classz.newInstance();//实例对象
             Iterator<String> it =  map.keySet().iterator();
             while(it.hasNext()) {
                 String key = it.next();
                 Object value = map.get(key);
                 Field field = classz.getDeclaredField(key);//获取map集合中的key,也就是实体的属性名
                 field.setAccessible(true);//打破private修饰规则
                 field.set(obj,value);
             }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return obj;
    }
}

5、动态代理(目标类必须实现接口)

Proxy(反射)的newProxyInstanceof(Class classLoader,Class [] interfaces,InovationHandler handler)方法,可以创建一个指定类(接口,mybatis的dao层就是这样)的代理类,也就是通过class对象获取到它实现的接口和类加载,再根据我们自己实现的InvocationHandler接口的invoke方法,创建代理对象(是接口的实现类)),当代理对象执行方法时,就会触发invoke方法

2)目标类以及目标类实现的接口

①目标类UsbKingFactory

public class UsbKingFactory implements UsbSale {
    @Override
    public float sale(int amount) {
        System.out.println("目标类的方法执行了,");
        return 85f;
    }

    @Override
    public void print() {
        System.out.println("您成功的买了U盘");
    }
}

②接口UsbSale

public interface UsbSale {
    public float sale(int amount);

    public void print();
}

2)InvocationHandler实现类

public class MyInvocationHandler implements InvocationHandler {


    private Object target;

    public MyInvocationHandler(Object target){
        this.target=target;//在实例化时,获取到目标类,这样就可以在代理时获取到目标的方法
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        float price = -1;
        Object res = null;
        res = method.invoke(target,args);//首先调用目标类的方法,保证基本方法执行
        if(null!=res){//设置自己代理对象的特殊效果
            price = (float)res+25;
        }
        return price;
    }
}

三、泛型

1、理解

  ①在JDK1.5之前,集合中所能存储的类是不确定的,可以存储任意类型(类似图书馆图书分类,垃圾箱分类一个道理),一个集合中的内容太杂。为了解决这一问题,泛型油然而生

②泛型的声明和作用域:
就是可以在声明类,接口时通过一个标识符表示或者是在方法中定义的一种数据类型,用该标识代表的这种数据类型,可以用在这个数据类型所能用的任何地方(定义变量,方法返回值,不可用于静态变量和静态方法的返回值,静态方法不可用类的泛型,catch异常捕捉不可用,在我们变量声明和方法的形参类型中指定标识对应的数据类型,如果没有指定,默认是Object类型

③子类可以继承父类

class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {

④通配符?,表示无法确定的类型,也就是可以为任意类型(和普通声明一样,但是无法存入信息,但是可以通过变量直接赋值),但是无法直接存入指定类型的元素,但是可以取出类型,因为都属于Object子类

public class TestOther {
    public static void main(String[] args) {
        List list = new ArrayList();
        List<?> list2 = list;//==>List<?> list3 = list;
    }
}

约束:List<? extends 类/接口> list , List<? super 类/接口> list,

⑤优点:在集合中取出元素不需要强转,并且可以自动检测元素内容是否符合要求。

2、泛型的使用

1)集合

不使用泛型:无法确定一个指定集合中的元素类型,数据需要强转

public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(123);
        list.add(23);
        list.add(12);
        list.add(13);
        list.add("score");//存入了错误的数据,
        for(Object obj : list){
            Integer score = (Integer) obj;//取出数据是需要强转,如果不是int类型,那么会出错
            System.out.println(score);
        }
    }
}

②使用泛型:类型不符合,编译时出错

/**
 * 一个存储成绩的集合
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList();
        list.add(123);
        list.add(23);
        list.add(12);
        list.add(13);
        //list.add("score");//存入了错误的数据,编译时出错
        for(Integer score : list){
            System.out.println(score);
        }
    }

2)自定义泛型

①类/接口
public class MyClass<T> {
    private T t;
    public static void main(String[] args) {
        MyClass<Date> myClass = new MyClass();
        myClass.t=new Date();
        System.out.println(myClass.t);
    }
}
②方法
 public <R> R doSome(R r){return r;}//只在方法内部有效
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值