第一节 为什么需要使用反射
正常在编写源程序时,手动使用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);
}
}