反射

第一节 为什么需要使用反射

正常在编写源程序时,手动使用new关键字创建对象,不利于程序后期的扩展和维护,程序的通用性特别差
反射: 有利于程序后期的扩展性
使用反射技术编写新的框架

package com.bjsxt.test;
import com.bjsxt.entity.Student;
public class Test1 {
    public static void main(String[] args)throws  Exception {
        //(1)正常编写程序
        Student student=new Student();
        System.out.println(student);//默认调用toString方法
        //(2)使用反射创建Student类的对象
        Class aClass=Class.forName("com.bjsxt.entity.Student");
        Object o = aClass.newInstance();  //向上类型转换
        System.out.println(o); //方法重写
    }
}

第二节 反射的作用

反射的作用:
①动态创建对象
②动态操作属性
③动态调用方法

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
◆Class类:代表一个类
◆Constructor 类:代表类的构造方法
◆Field 类:代表类的成员变量(属性)
◆Method类:代表类的成员方法

Class类位于java.lang包
Class类的类表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注释是一种interface。 每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。 原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象。

/**
 *
 */
public class Test {
    public static void main(String[] args) {
        //【1】同一个类的对象,共享同一个Class对象
        Student student=new Student("1001","张三");
        Student student1=new Student("1002","李四");
        //以下两个Student类的对象student和student1共享同一个student.class
        System.out.println(student.getClass()==student1.getClass());

        //【2】维数相同,数据类型相同的数组共享同一个Class对象
        int[] arr=new int[5];
        int[] arr1={1,2,3,4};
        System.out.println(arr.getClass()==arr1.getClass());

        //基本数据类型和类的关键字void也表示为类的对象
        Class<Integer> integerClass = int.class;
        Class<Void> voidClass = void.class;
    }
}

第三节 Class类对象获取的几种方式

package com.bjsxt.test;

import com.bjsxt.entity.Student;

/**
 *Class类对象获取的几种方式
 */
public class TestClass {
    public static void main(String[] args) throws Exception {
        //【1】Class类的静态方法
        Class aClass=Class.forName("com.bjsxt.entity.Student");

        //【2】通过类的字节码文件
        Class studentClass=Student.class;

        //【3】通过类的成员方法getClass(),该方法是从Object父类继承而来
        Student stu=new Student();
        Class aClass1=stu.getClass();

        System.out.println(aClass==studentClass);
        System.out.println(aClass==aClass1);
        System.out.println(aClass);

        //包装类标准使用的是TYPE,但是包装类也是类也可以用字节码文件
        Class<Integer> integerClass = Integer.class; //包名+类名  class java.lang.Integer

        Class<Integer> integerClass1 = int.class;  //int
        System.out.println(integerClass==integerClass1);  //false
        System.out.println(integerClass);
        System.out.println(integerClass1);

        Class<Integer> type = Integer.TYPE; //包装类获取Class对象的标准方式
        System.out.println(type);
        System.out.println(integerClass1==type);  //true
        System.out.println(type);  //int
    }

}

Class类常用的方法
在这里插入图片描述

**
 *Class类的常用方法
 */
public class Test4 {
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("java.util.ArrayList");
        System.out.println("报名+类名:"+aClass.getName());  //报名+类名:java.util.ArrayList
        System.out.println("获取package相关信息:"+aClass.getPackage()); //获取package相关信息:package java.util, Java Platform API Specification, version 1.8
        System.out.println("获取ArrayList的父类:"+aClass.getSuperclass()); //获取ArrayList的父类:class java.util.AbstractList
    }
}

第四节 Field对象

一个属性对象由以下几部分组成:
①修饰符 ,修饰符的类型为Modifier
②属性的类型 [基本数据类型,引用数据类型]
③属性的名称 [第一个单词首字母小写,第二个单词开始首字母大写]

package com.bjsxt.test;

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

/**
 * 属性对象
 */
public class TestField {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //【1】获取类的Class对象
        Class aClass = Class.forName("com.bjsxt.entity.Student");
        //【2】获取Student类的属性对象
        Field[] fields = aClass.getFields();  //获取类的public属性
        System.out.println(fields.length);

        Field[] declaredFields = aClass.getDeclaredFields();//获取类的所有属性
        for (Field field:declaredFields) {
            //Field重写了toString方法,所以输出结果为:(其中一个属性)private java.lang.String com.bjsxt.entity.Student.stuId
            //System.out.println(field);
            //一个属性对象由几部分组成
            System.out.println("属性的修饰符:"+field.getModifiers()+"修饰符对象"+ Modifier.toString(field.getModifiers()));//注意【1】field.getModifiers的返回值是一个int类型;【2】注意这个toString不是obj那个
            System.out.println("属性的数据类型:"+field.getType());
            System.out.println("属性的名称:"+field.getName());
            System.out.println("------------------------------我是分割线-----------------------------------");

            //根据属性名称获取指定的属性对象
            Field stuId = aClass.getDeclaredField("stuId");
            System.out.println(Modifier.toString(stuId.getModifiers())+"\t"+stuId.getType()+"\t"+stuId.getName());
            //aClass.getField(String --);  //获取public修饰的属性
        }

    }
}

第五节 构造方法对象

一个类的构造方法由几部分组成
①修饰符 Modifier对象
②名称 [与类名相同]
③参数列表 Parameter对象

package com.bjsxt.test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

/**
 *构造方法对象
 */
public class TestConstructor {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        //【1】获取Student类的Class对象
        Class aClass = Class.forName("com.bjsxt.entity.Student");
        //【2】获取Student类的构造方法对象
        Constructor[] constructors = aClass.getConstructors();
        for (Constructor constructor:constructors) {
            //System.out.println(constructor);    //  public com.bjsxt.entity.Student()
            System.out.println("构造方法修饰符:"+ Modifier.toString(constructor.getModifiers()));
            System.out.println("构造方法的名称:"+constructor.getName());
            System.out.println("构造方法的参数");
            Parameter[] parameters = constructor.getParameters();
            for (Parameter parameter:parameters) {
                System.out.println("参数的权限:"+parameter.getModifiers());
                System.out.println("参数的类型:"+parameter.getType());
                System.out.println("参数的名称:"+parameter.getName());
            }
            System.out.println("-----------------------我是分割线---------------------------------");
        }

        //获取Student类的指定的构造方法
        Constructor constructor1 = aClass.getConstructor();//获取Student类无参的构造方法
        Object o = constructor1.newInstance();
        System.out.println(o);     //输出:Student{stuId='null', stuName='null'}
        //获取Student类的带参数的构造方法
        Constructor constructor = aClass.getConstructor(String.class, String.class);
        Object o1 = constructor.newInstance("sxt1001", "张三");
        System.out.println(o1);    //输出:Student{stuId='sxt1001', stuName='张三'}

    }
}

第六节 方法对象

上午内容复习:
掌握 一个主线
反射: a)创建类的对象 b)操作类的属性 c)操作类的方法
学习4个类:Class -->所有类类型的模板 ,因为Class中说一个类需要具备属性,方法,构造方法
Field -->所有的属性的对象
Constructor -->所有的构造方法的对象
Method–>所有的方法的对象

       增两类: Modifier --->所有修饰符的对象
                   Parameter -->所有的参数的对象

下午内容:方法对象

package com.bjsxt.test;

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

/**
 *方法对象
 */
public class TestMethod {
    public static void main(String[] args) throws ClassNotFoundException {
        //【1】获取Student的Class对象
        Class aClass = Class.forName("com.bjsxt.entity.Student");
        //获取Student类的方法
        Method[] methods = aClass.getMethods();  //获取public方法,包含从父类继承过来的方法
        for (Method method:methods) {
            System.out.println(method);  //包括父类的方法
        }
        System.out.println("---------获取本类中定义的方法(公共,保护,默认(包)访问和私有方法,但不包括继承的方法)------------");
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod:declaredMethods) {
            System.out.println(declaredMethod);
            System.out.println("方法的修饰符:"+ Modifier.toString(declaredMethod.getModifiers()));
            System.out.println("方法的返回值类型:"+declaredMethod.getReturnType());
            System.out.println("方法的名称:"+declaredMethod.getName());
            System.out.println("方法的参数列表:");
            Parameter[] parameters = declaredMethod.getParameters();
            for (Parameter parameter:parameters) {
                System.out.println(Modifier.toString(parameter.getModifiers())+"\t"+parameter.getType()+"\t"+parameter.getName());
            }
            System.out.println("----------------------------------------------------------------");
        }

    }
}

**课堂案例:**不使用new关键字创建Student类的对象,并执行相应的方法(使用反射)

package com.bjsxt.test;

import com.bjsxt.entity.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *实例:不使用new关键字创建Student类的对象,并执行相应的方法(使用反射)
 */
public class TestStudent {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //【1】获取Student类的Class对象
        Class aClass = Class.forName("com.bjsxt.entity.Student");
        //【2】创建Student类的对象
        //(a)使用Class的newInstance方法
        Student o =(Student) aClass.newInstance();

        //(b)先获取Student的构造方法对象,在通过构造方法对象的newInstance()方法得到Student类的对象
        Constructor constructor = aClass.getConstructor();
        Object o1 = constructor.newInstance();

        //【3】获取赋值的方法对象
        Method setStuId = aClass.getMethod("setStuId", String.class);
        Method setStuName = aClass.getMethod("setStuName", String.class);

        //【4】执行赋值的方法给属性赋值
        setStuId.invoke(o,"sxt1001");
        setStuName.invoke(o,"张三");

        //【5】调用toString方法
        Method toString = aClass.getMethod("toString");
        Object invoke = toString.invoke(o);
        System.out.println(invoke);

        System.out.println("----------------------以上代码可以用一句正常代码实现----------------------------");
        System.out.println(new Student("sxt1001","张三"));
    }
}

第七节 反射的应用

一、反射对泛型的影响

泛型 :JDK1.5开始
泛型的作用:用于在编译之前,举例,对集合中存入数据类型进行验证

package com.bjsxt.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 *反射对泛型的影响
 */
public class TestGer {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<String> arrayList=new ArrayList<>();
        arrayList.add("hello");
        //arrayList.add(123);
        System.out.println(arrayList);
        //获取arrayList的Class对象
        Class aClass = arrayList.getClass();
        //可不可以得到add方法
        Method add = aClass.getMethod("add", Object.class);
        //使用反射执行add方法
        add.invoke(arrayList,123);

        System.out.println(arrayList);
        System.out.println(arrayList.size());
        /**为什么使用反射可以将int类型的123添到集合中,因为int--自动装箱为Integer, -->向上类转换,转成Object类型,
         * 而ArrayList底层本身就是Object类型数据*/
    }
}

为什么使用反射能将124添加进去?
◆因为int–自动装箱为Integer, -->向上类转换,转成Object类型, 而ArrayList底层本身就是Object类型数据。所以123是可以添加到集合里面去的,但是由于List使用了泛型,我们没办法添加, 但是通过反射可以绕过泛型,从而添加进去。
泛型只在编译期间起作用,反射是在程序运行的时候才去创建对象(反射在程序运行的时候才去执行的),编译以后是没有泛型的,反射是执行的编译之后的结果,是没有泛型的,所以123可以添加进去。也就是说这个123不是在编译的时候添加的,而是通过程序运行的时候动态添加进去的。

二、反射对设计模式的影响

设计模式 :反复使用的,被多数人知晓的,对于特定情况的,针对性的解决方案
Java一共23种设计模式

工厂设计模式:属于创建型设计模式(就是创建对象)
多态的两种表现形式
(1)父类作方法的形参,可以传入任意的子类对象
(2)父类作方法的返回值,【称为工厂设计模式】

1.0版的工厂设计模式

Animal接口和几个子类

package com.bjsxt.pattern;
//Animal接口
public interface Animal {
    void shout();
}
//多个Animal子类
class Dog implements Animal{
    @Override
    public void shout() {
        System.out.println("汪汪.....");
    }
}

class Cat implements  Animal{
    @Override
    public void shout() {
        System.out.println("喵喵......");
    }
}

class Pig implements Animal{
    @Override
    public void shout() {
        System.out.println("哼哼.....");
    }
}

class GoldFish implements  Animal{
    @Override
    public void shout() {
        System.out.println("吐泡泡。。。");
    }
}

工厂模式 (父类作为返回值,指向子类对象)

package com.bjsxt.pattern;

/**
 *工厂模式1.0版
 */
public class AnimalFactory{
    public static Animal newInstance(int choice){
        Animal animal=null;
        switch(choice){
            case 1:
                animal=new Dog();
                break;
            case 2:
                animal=new Cat();
                break;
            case 3:
                animal=new Pig();
                break;
        }
        return  animal;
    }
}

测试类

package com.bjsxt.pattern;

import java.util.Scanner;

/**
 * 1.0工厂模式测试类
 */
public class Test {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        for (int i = 0; i < 5; i++) {
            System.out.println("请输入1-狗,2-猫,3-猪");
            int choice=sc.nextInt();
            Animal animal= AnimalFactory.newInstance(choice);
            animal.shout();
        }
    }
}

缺点:不利于程序的后期扩展,一旦有新的子类产生,将不断的修改源代码(增加case分支)

2.0版的工厂设计模式
使用反射来实现,无论新增多少子类,工厂中的代码,一动不动

//Animal和其子类与1.0版本的相同(见上面,这里省略)

工厂模式2.0

package com.bjsxt.pattern;

/**
 * 工厂模式2.0版
 */
public class AnimalFactory2 {
    public static Animal newInstance(String str) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class aClass = Class.forName(str);
        return (Animal) aClass.newInstance();
    }
}

测试类

import java.util.Scanner;

/**
 * 2.0版本测试类
 */
public class Test2 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        Scanner sc=new Scanner(System.in);
        for (int i = 0; i <5 ; i++) {
            System.out.println("请您输入英文字符串(以后会从xml文件中读取【.txt文件中读取】)");
            String str=sc.next();
            str="com.bjsxt.pattern."+str;
            Animal animal = AnimalFactory2.newInstance(str);
            animal.shout();
        }
    }
}

**优点:**扩展性强,无论扩展多少 子类,工厂类都“一动不动”无需做任何的更改

三、使用反射封装JDBC

1.0版本(查询数据库中的所有数据,相当于遍历),没有使用使用反射,复用率低,在查询不同表的时候,需要重写代码

创建dept表对应的类Dept

package com.bjsxt.jdbc;

/**
 * Dept类
 */
public class Dept {
    private int deptno;
    private String dname;
    private String loc;

    public int getDeptno() {
        return deptno;
    }

    public void setDeptno(int deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    public Dept(int deptno, String dname, String loc) {
        this.deptno = deptno;
        this.dname = dname;
        this.loc = loc;
    }

    public Dept() {
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno=" + deptno +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}

封装JDBC查询

package com.bjsxt.jdbc;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class JDBCUtil {
    private static final String DRIVER = "com.mysql.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost:3306/syl1105";
    private static final String USERNAME = "root";
    private static final String PWD = "root";

    //获取连接对象
    public static Connection getConn(){
        Connection connection=null;
        try {
            Class.forName(DRIVER);
            connection= DriverManager.getConnection(URL,USERNAME,PWD);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  connection;
    }

    //获取结果集
    public static ResultSet getResultSet(String sql){
        //(1)获取连接对象
        Connection conn = getConn();
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        //(2)创建
        try {
            statement = conn.prepareStatement(sql);
            resultSet = statement.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return  resultSet;
    }

    //查询(查询所有,相当于遍历了)
    public static List queryAll(ResultSet resultSet){
        List list=new ArrayList();
        try {
            while (resultSet.next()){
                Dept dept=new Dept();
                int deptno = resultSet.getInt("deptno");
                String dname=resultSet.getString("dname");
                String loc = resultSet.getString("loc");

                dept.setDeptno(deptno);
                dept.setDname(dname);
                dept.setLoc(loc);

                list.add(dept);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return  list;
    }

    //封装关闭全部的方法
    public static void closeAll(ResultSet resultSet, Statement statement, Connection connection){
        if(resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(statement!=null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
}

测试类

package com.bjsxt.jdbc;

import java.sql.ResultSet;
import java.util.List;

public class TestQueryAll {
    public static void main(String[] args) {
        String sql="select * from dept";
        ResultSet resultSet = JDBCUtil.getResultSet(sql);
        List depts = JDBCUtil.queryAll(resultSet);
        System.out.println(depts);

    }
}

2.0版本,使用反射,不同的表都可以查询,无需修改代码

package com.bjsxt.jdbc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JDBCUtil2 {
    private static final String DRIVER = "com.mysql.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost:3306/syl1105";
    private static final String USERNAME = "root";
    private static final String PWD = "root";

    //获取连接对象
    public static Connection getConn(){
        Connection connection=null;
        try {
            Class.forName(DRIVER);
            connection= DriverManager.getConnection(URL,USERNAME,PWD);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  connection;
    }

    //
    public static ResultSet getResultSet(String sql){
        //(1)获取连接对象
        Connection conn = getConn();
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        //(2)创建
        try {
            statement = conn.prepareStatement(sql);
            resultSet = statement.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return  resultSet;
    }

    //查询(查询所有,相当于遍历了)
    public static<T> List<T> queryAll(ResultSet resultSet,Class<T> clazz){  //泛型方法,T表示一种数据类型(引用数据类型)
        List<T> list=new ArrayList<>();
        try {
            while (resultSet.next()){  //循环一次,获取一行
                T bean=clazz.newInstance();  //调用无参构造方法创建对象
                //分解行中的列
                //既然类型不知道、名称不知道,需要使用反射获取T这个类型的属性有哪些?因为属性与数据库中个的字段一一对应。
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field field:declaredFields) { //问:能不能得到属性的名称
                    String fieldName=field.getName();  //得到属性的名称,[对应的是数据库中表中的字段名称]
                    Object value=resultSet.getObject(fieldName);  //放到getObject中,fileName实际上是数据库中字段的名称,根据名称获得字段对应的值。
                    //上面做的相当于这句:int deptno = resultSet.getInt("deptno");
                    //字符串的拼接  赋值方法的名称 set+属性名(第一个字母大写)
                    String setMethodName="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
                    //System.out.println(setMethodName); //用于测试
                    //通过反射获取赋值方法的对象
                    //Method method = clazz.getMethod(setMethodName, Object.class);//不通用,因为赋值的方法的参数的类型 不一样,而且int属于基本数据类型cl
                    Method method = clazz.getMethod(setMethodName, field.getType());
                    //System.out.println("获取方法对象:"+method);
                    //执行赋值方法,给属性赋值
                    method.invoke(bean,value);  //相当于:dept.setDeptno(deptno);

                }  //整个for循环结束,赋值结束
                //添加到集合中
                list.add(bean);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return  list;
    }

    //封装关闭全部的方法
    public static void closeAll(ResultSet resultSet, Statement statement, Connection connection){
        if(resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(statement!=null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
}

测试类

package com.bjsxt.jdbc;

import java.sql.ResultSet;
import java.util.List;

public class TestQueryAll2 {
    public static void main(String[] args) {
        String sql ="select * from dept";
        ResultSet resultSet = JDBCUtil2.getResultSet(sql);
        List<Dept> depts = JDBCUtil2.queryAll(resultSet,Dept.class);
        System.out.println(depts);

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值