SqlServer:不支持java语言
模块之间的关系——耦合度(高内聚、低耦合)
类的加载过程:
定义:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制,拿到字节码文件即可获取对应的类的代表对象
1. .java文件------->(编译).class文件---->加载到方法区----->对class文件进行封装
**反射----解析类,**通过字节码对象来获取实例对象的过程(反射一定是发生在运行时期的,编译期间是不可能发生发射现象的)
《在JAVA眼中,万物皆都对象》
1.Class —>代表类的类(产生的对象就是一个具体的类)(字节码对象)
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
2.Field—>代表类的属性的类(产生的对象就是一个具体的属性)
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
3.Method—>代表方法的类(产生的对象就是一个具体的方法)
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
4.ConStructor—>代表构造方法的类(产生的对象就是一个具体的构造方法)
Constructor 提供关于类的单个构造方法的信息以及对它的访问权限
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的
对象是类的具体实现
获取类的字节码对象:三种方式
获取字节码对象的方式一: 类名.class—>任何一个类都能得到对应的字节码对象(加载进了内存)
String,StringBuilder,StringBuffer都是最终类,没有子类
class类也是Object类的子类,class产生的事指定类型的类的类对象
//获取字节码对象
//String类的字节码对象
Class<String> clz = String.class;
System.out.println(clz);//class java.lang.String
//接口中的字节码对象
Class<List> clz1 = List.class;
System.out.println(clz1);//interface java.util.List
//基本类型的字节码对象 不在任何包下的对象
Class<Integer> clz2 = int.class;
System.out.println(clz2);//int
获取字节码对象方式二: 通过对象获取字节码文件,已经加载进了内存并且创建了对象 对象.getClass().
静态资源,可以直接通过类名,进行调用,可以用来获取代表类的对象,通过类的对象,可以获取对象的属性
static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。
static Class<?> forName(String name, boolean initialize, ClassLoader loader)
//基本类型的字节码对象 基本类型的class对象的泛型是包装类
//因为里面接收的是对象的类型,int不能创建对象,包装类才有对象
Class<Integer> in=int.class;
//2.对象获取字节码对象 方法的强转类型----返回是固定类型,类型不一致就需要进行强转
Class<String> clz3 = (Class<String>) "abc".getClass();
System.out.println(clz3);//class java.lang.String
//3.通过类对象来创建对象 通过使用类对象进行获取字节码文件的时候,需要进行类型的判断
//当加载的是类的时候,才能进行相关的class字节码,因为只有类才有字节码对象
//所以当报错找不到类的时候,只有可能是类名灭有进行写对
Class<?> c2 = forName("java.lang.reflect.Constructor");
System.out.println(c2);
获取字节码文件方式三: 通过加载获取类的全限定类名进行加载进内存,从而产生字节码文件,有个字节码文件之后才有字节码的对象(还没加载到内存当中,需要将.java文件加载进入内存) Class.forName(“全限定类名”)
为什么要是全限定类名的呢?不能保证不同的包的文件中的类名存在雷同的现象
//3.必须写入路径类名,因为java中不同的包可能会出现同一个类名,所以不能只写类名
Class<List> clz4= (Class<List>) Class.forName("java.util.List");
System.out.println(clz4);//interface java.util.List
原始数据类型一般是基本数据类型:int double…
相关方法的的应用都是获取相关的属性,相关的方法:
//获取String类的class对象
Class<String> clz = String.class;
//获取所有的实现的接口或者继承的街路口
Class<?>[] cs = clz.getInterfaces();
for (Class<?> c : cs) {
System.out.println(cs);
}
//获取相关的包名
Package pack = clz.getPackage();
System.out.println(pack);
//获取String类的父类
Class<? super String> super1 = clz.getSuperclass();
System.out.println(super1);
//判断是否是内部类
System.out.println(new Object().getClass().isAnonymousClass());
//判断是否是基本数据类型---->也就是原始数据类型
System.out.println(int.class.isPrimitive());
获取实例对象的方式:
- 获取字节码文件对象(代表类的类)
- 使用字节码对象获取构造方法:对象的产生是由构造方法构建的
- 使用构造方法创建对象并返回该对象(构造方法:有参构造和无参构造,有部分类没有无参构造方法)
- 操作该对象
getConstructor()—getDeclaredConstructor()的区别:
- getConstructor方法的获取的是公共的方法,接口中和公开构造方法
- getDeclaredConstructor()方法可以获取到类中的公共的方法和私有的方法,常理都可以进行获取操作
- 存在部分的语言访问权限指令:
- setAccessible(true):暴力破解,进行暴力反射能够越级访问,那个区域需要暴力访问,那么就需要就应该调用该方法,避免出现存在私有的方法或者默认的访问权限
getField()—getDeclaredField()的区别;
- getField()获取的是公共的属性,接扣中或者类中的公共属性
- getDeclaredField()可以获取所有的声明的属性,公共和私有的都可以进行获取,一般都调用这个方法。
- - 存在部分的语言访问权限指令:
- setAccessible(true):暴力破解,进行暴力反射能够越级访问,那个区域需要暴力访问,那么就需要就应该调用该方法,避免出现存在私有的方法或者默认的访问权限,能够进行私有属性的修改
- set(Object obj,object obj) 和 get(Object obj )---->可以用来获取或者修改类中的属性
- field.get(obj) filed.set(obj,obj)
//通过字节码对象来获取实例对象
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//获取字节码对象
Class<String> clz=String.class;
//通过字节码对象获取实例对象
//执行无参构造的方法创建无参对象 有可能抛出的异常是不合法的异常,受访问权限的yuanyin
String s=clz.newInstance();
//创建的对象返回的是空串在底层的实现
System.out.println(s);
//先获取有参构造
//参数类型需要传入的是以字节码的形式来传入才能找到的数据
//因为底层的String是由java进行提供的,获取的是原来已经在底层的公共方法
//所以使用普通的getConstructor方式进行获取即可
Constructor<String> c1=clz.getConstructor(String.class);
//通过构造方法获取实例对象 c1已经确定了对象的对象的类型,所以接收的时候不需要进行强制类型的转换
//执行有参构造并且返回实例对象
String s2=c1.newInstance("颤三");
System.out.println(s2);
//通过反射来获取INteger的对象
//1.先获取Integer的字节码对象----》Integer类没有无参构造方法
Class<Integer> in=Integer.class;
//2.通过字节码对象来获取构造方法
Constructor<Integer> in2=in.getConstructor(int.class);
//3.通过构造方法来获取对象
//3.1对象的创建,都是来自于构造方法的初始化进行创建
Integer in3=in2.newInstance(123);
//打印出Integer对象--->实现自动拆箱的过程,在1.5
System.out.println(in3);
//通过实现自动拆箱的过程
System.out.println(in3.intValue());
}
暴力反射的案列
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<String> clz=String.class;
//通过字节码对象获取构造方法
Constructor c1=clz.getDeclaredConstructor(char[].class,boolean.class);
//通过构造方法创建对象, 关闭语言访问检查机制,对此构造方法
c1.setAccessible(true);
//将字符序列装欢成字符串的形式
String s= (String) c1.newInstance(new char[]{'1','2'},true);
//打印d对象
System.out.println(s);
}
}
反射中的强制修改属性的方法:
public class ClassDemo4 {
//通过暴力反射改变类的值
//知名反射是强大的,没有阻碍进行数据访问权限
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//获取私有化属性
Class<String> clz=String.class;
//对象才能进行属性的访问
//获取构造方法 属性也是一个对象,由自己的hashcode值
Field field=clz.getDeclaredField("hash");
//对象存在hashcode值
String str="abc";
//获取hashcode的值
System.out.println(str.hashCode());
//进行hashcode的值的修改
//实现暴力反射
field.setAccessible(true);
//进行值的修改 Set(Object obj,int val)
//第一个参数 表示要修改的是哪个对象,后面表示的是修改的值
field.set(str,1234);
//获取hashcode的值 field表是hash属性的对象,通过对象可以确定hash的归属
System.out.println(field.get(str));
System.out.println(str.hashCode());
}
}
Method对象的使用: 方法的调用使用 invoke(Object obj,...)
public class ClassDemo5 {
public static void main(String[]args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//底层的方法的反射结构
//获取字节码文件对象
Class<String> clz=String.class;
//获取指定的方法
//第一个参数 方法的名字
//第二个参数 方法参数的字节码文件对象
Method m=clz.getDeclaredMethod("charAt", int.class);
//前提是要获取到该对象,动态代理模式中常用
String str="asdjkagdsg";
//执行方法,基于对应的类进行执行方法
//第一个参数 调用该方法的对象
//第二个参数 方法中需要传递的参数
char c= (char) m.invoke(str,6);
System.out.println(c);
}
}
反射的缺点:
- 打破封装原则:所有的东西都可以继续获取,私有化也可以进行获取
- 跳过泛型的类型检测
缺点的实现:
反射在编译期跳过了编译检测数据类型
public class ClassDemo7 {
//实现反射跳过类型检测的现象
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//规定类型是string类型
List<String> list=new ArrayList<>();
list.add("abc");
//奇迹出现
//获取上面对象的字节码文件
Class<List> clz= (Class<List>) list.getClass();
//获取List集合中的方法
Method m=clz.getDeclaredMethod("add", Object.class);
//进行数据元素的添加
m.invoke(list,123);
m.invoke(list,true);
System.out.println(list);
//原本在创建对象的时候,指定的类型是String,但是其他类型也能进行添加,说明跳过了类型的检测机制
//编译的时候h会有泛型不会进行类型的检测,如果不符合数据类行就会检错
//在运行期间反射改变存储数据元素类型,直接跳过编译检测,也是就是说在反射
}
}
案列——实现克隆方法(clone()):
无参构造的属性克隆操作:
package cn.tedu.reflect01;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemoTest {
//实现底层的clone方法
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
//开始测试
Person p=new Person();
p.setAge(19);
p.setName("张元宝");
//进行克隆对象
Person person= (Person) clone(p);
//打印结果数据
System.out.println(p);
System.out.println(person);
}
public static Object clone(Object obj) throws IllegalAccessException, InvocationTargetException, InstantiationException {
//创建原来对象字节码对象
Class<Object> clz= (Class<Object>) obj.getClass();
//获取实例对象,通过字节码对象实现无参构造对象
//至少获取到了一个构造方法
Constructor c=clz.getDeclaredConstructors()[0];
//通过构造方法创建对象
Object obj1=c.newInstance();
//通过字节码文件对象获取所有的属性
Field[] field=clz.getDeclaredFields();
//进行遍历属性,将属性每个进行一一复制的操作
for (Field f : field) {
//暴力反射,比买按属性没有被复制过去
f.setAccessible(true);
//获取原来对象的属性值
Object value=f.get(obj);
//进行属性的复制操作
f.set(obj1,value);
}
//属性复制结束后,进行新对象的返回
return obj1;
}
}
class Person{
private String name;
private int age;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
带参构造的属性的克隆操作:
package cn.tedu.reflect01;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemoTest2 {
//实现底层的clone方法 升级版的形成,可以进行克隆带参数的构造方法
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
//进行测试
Student s=new Student();
s.setName("弘历");
s.setAge(20);
//进行克隆的复制
Student s1= (Student) clone(s);
System.out.println(s);
System.out.println(s1);
}
public static Object clone(Object obj) throws IllegalAccessException, InvocationTargetException, InstantiationException {
//创建原来对象字节码对象
Class<Object> clz= (Class<Object>) obj.getClass();
//获取实例对象,通过字节码对象实现无参构造对象
//至少获取到了一个构造方法
Constructor c=clz.getDeclaredConstructors()[0];
//通过构造方法创建对象
// Object obj1=c.newInstance();
//获取构造方法上的所有的参数类型的对象表示 按照声明的类型返回变量的类型,同时获取参数的对象字节码文件对象
Class[] parms=c.getParameterTypes();
//提供数组进行存储有参构造参数的初始化值---->提供数组存储函数中中参数的舒适化的值
Object[] os=new Object[parms.length];
//遍历数组,遍历的是参数列表的数组
for (int i = 0; i < parms.length; i++) {
//进行类型的判断
if (parms[i].isPrimitive()){
//判断是否是元数据类型(基本数据类型)
if (parms[i]==byte.class||parms[i]==short.class||
parms[i]==int.class){
os[i]=0;
}else if (parms[i]==char.class){
os[i]='\u0000';
}else if (parms[i]==long.class){
os[i]=0L;
}else if (parms[i]==float.class){
os[i]=0.0f;
}else if (parms[i]==double.class){
os[i]=0.0;
}else{
os[i]=false;
}
}else{
//引用数据类型
os[i]=null;
}
}
//获取构造函数参数对象
//执行构造函数并返回对象 构造方法中存放的是数组的对象,里面存放的是构造函数的参数
//传递的时候传递的是数组的对象,在构造函数中进行体现,传入参数的类型
Object obj2=c.newInstance(os);
//通过字节码文件对象获取所有的属性
Field[] field=clz.getDeclaredFields();
//进行遍历属性,将属性每个进行一一复制的操作
for (Field f : field) {
//暴力反射,比买按属性没有被复制过去
f.setAccessible(true);
//获取原来对象obj的属性值——->获取原来的传递过来的对象进行获取它的属性值
Object value=f.get(obj);
//进行属性的复制操作 新对象进行值的设定操作
f.set(obj2,value);
}
//属性复制结束后,进行新对象的返回
return obj2;
}
}
class Student{
private String name;
private int age;
public Student(){}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}