Java反射之创建对象的四种方式

Java反射之创建对象的四种方式,第1张

Java反射之创建对象的四种方式

1.使用new关键字

2.使用Java反射机制,反射构造器

3.使用克隆方式创建对象Cloneable

4.使用序列化Serializable

1.使用Java反射机制创建对象

1.1.创建用户对象

public class Users {
	
	private int id =1001;
	private String name ="张三";
	private int age = 24;
	
	public Users() {
		super();
	}

	public Users(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	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 "Users [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

}

1.2.使用Java反射机制创建对象

/**
 * Java反射的方式创建对象
 * @author jkl
 *
 */
public class TestForName {

	public static void main(String[] args) throws Exception, SecurityException {
		try {
			 //1.基于Java反射机制创建对象
			Class cl =	Class.forName("com.gun.classforName.Users");
			Users user;
			try {
				user = (Users)cl.newInstance();
				System.out.println("用户ID:"+user.getId());
				System.out.println("姓名:"+user.getName());
				System.out.println("年龄:"+user.getAge());
				//2.基于Java的反射机制获取构造函数创建对象
				Constructor con = cl.getConstructor();
				Users user2 =(Users)con.newInstance();
				System.out.println(user.getAge());
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

1.3.输出结果

用户ID:1001
姓名:张三
年龄:24
24
2.使用克隆方式创建对象Cloneable

2.1创建用户对象

public class UserDto implements Cloneable {
	private int id = 202204024;
	private String name ="王美玲";
	private int age = 18;
	
	public UserDto() {
		super();
	}

	public UserDto(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	///把这个方法重写以下就行,直接使用原有的就行,不用继续修改
	@Override
	protected UserDto clone() throws CloneNotSupportedException {
		UserDto u1 =(UserDto) super.clone();
		return u1;
	}
 

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	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 "UserDto [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		UserDto other = (UserDto) obj;
		if (age != other.age)
			return false;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
}

2.2测试克方式创建对象

/**
 * 通过克隆方式创建对象
 * @author jkl
 *
 */
public class TestColne {
	public static void main(String[] args) throws CloneNotSupportedException {
		
		UserDto dto = new UserDto();
		//通过克隆方式创建对象
		UserDto dto2 = dto.clone();
		
		System.out.println("用户ID:"+dto2.getId());
		System.out.println("姓名:"+dto2.getName());
		System.out.println("年龄:"+dto2.getAge());
		
	}

}

2.3.输出结果

用户ID:202204024
姓名:王美玲
年龄:18
3.使用序列化创建对象Serializable

3.1.创建序列化对象

import java.io.Serializable;
/**
 * 实现序列化接口
 * @author jkl
 *
 */
public class HolleDto implements Serializable {
	
	private int id;
	private String name;
	private int age;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	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;
	}
	public HolleDto(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	public HolleDto(){
		
	}
	@Override
	public String toString() {
		return "HolleDto [id=" + id + ", name=" + name + ", age=" + age + "]";
	}	

}

3.2.测试序列化创建对象

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 序列化方式创建对象
 * @author jkl
 *
 */
public class TestSerializable {

	public static void main(String[] args) throws Exception {
		HolleDto dto = new HolleDto();
		dto.setId(2022);
		dto.setAge(35);
		dto.setName("王米兰");
		outPutStream(dto);
		HolleDto dto2 = outPutStreamBack();
		System.out.println(dto==dto);// 返回 true : 表明两个对象是相对的
		System.out.println(dto.equals(dto2));//返回: false
		System.out.println(dto.hashCode());// 865113938
		System.out.println(dto2.hashCode()); //1808253012
		System.out.println(dto.toString());// HolleDto [id=2022, name=王米兰, age=35]
		System.out.println(dto2.toString()); // HolleDto [id=2022, name=王米兰, age=35]
	}
	
	//序列化对象
	public static void outPutStream(HolleDto dto){
		String path  = "G:\spring-work\java-juc\myguyuyun\holle.txt";
		File file = new File(path);
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(file);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(dto);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//反序列化对象
	public static HolleDto outPutStreamBack( ) throws ClassNotFoundException{
		String path  = "G:\spring-work\java-juc\myguyuyun\holle.txt";
			HolleDto dto = null ;
			File file = new File(path);
			FileInputStream fis ;
			ObjectInputStream  ois;
			try {
				fis = new FileInputStream(file);
				ois = new ObjectInputStream(fis);
				 dto =(HolleDto) ois.readObject();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			return dto;
		}
}

输出结果:

true
false
865113938
1808253012
HolleDto [id=2022, name=王米兰, age=35]
HolleDto [id=2022, name=王米兰, age=35]

序列后文件内容是字节码

4.总结

1,在实际开发中new和反射用的比较多,克隆相对用的比较少

2,序列序列后会将字节压缩为字节码,介绍占用空间,用于网络传输数据相对好些。

理论总结反射

当程序运行加载完之后,会存在堆内存中,产生一个Class类型的对象(一个类只有一Class对象,

如果一个类有匿名内部类就会产生两个Class对象),这个对象就包含了完整的类的结构信息,

我们可以通过这个类看到类对象的信息如:构造方法,get和set方法。

反射的概念:当我们照镜子时,透过镜子看到我们,这样称着为:反射。

反射的优点:

1,提高程序的灵活性和扩展性,降低代码的耦合性。

2,通过反射机制在程序运行时可以 *** 作类对象。

3,反射机制时构建框架技术的基础所在,使用反射可以避免讲代码写死在框架中。

反射的缺点:

尽管反射机制带来了极大的灵活性及方便性,但反射也有缺点。反射机制的功能非常强大,但不能滥用。在能不使用反射完成时,尽量不要使用,原因有以下几点:

1、性能问题

Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。

因此,反射 *** 作的效率要比正常 *** 作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。

而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。

2、安全限制。

使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。

3、程序健壮性。

反射允许代码执行一些通常不被允许的 *** 作,所以使用反射有可能会导致意想不到的后果。

反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,

代码产生的效果与之前会产生差异。

5.Java反射应用场景 5.1.加载数据库驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());Class.forName("com.mysql.cj.jdbc.Driver");
5.2xml或properties等配置文件加载

​ Spring通过XML配置模式装载Bean的过程

  • 将程序中所有XML或properties配置文件加载入内存
  • Java类里面解析xml或者properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息
  • 使用反射机制,根据这个字符串获得某个类的Class实例
  • 动态配置实例的属性

反射解析配置文件内容application.properties案例

配置文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/tx_cloud_pay?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

测试返回获取配置文件信息

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
/**
 * Java反射机制读取数据库链接配置文件
 * @author jkl
 *
 */
public class MyProperties {

	public static void main(String[] args) throws Exception {
		String path = "G:\spring-work\java-juc\myguyuyun\application.properties";
		Properties pro = new Properties();
		File file = new File(path);
		FileInputStream fis = new FileInputStream(file);
		pro.load(fis);
		//获取key值
		System.out.println(pro.getProperty("spring.datasource.driver-class-name"));
		System.out.println(pro.getProperty("spring.datasource.url"));
		fis.close();
	}
}

输出结果

com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/tx_cloud_pay?useUnicode=true&characterEncoding=utf8
6.java反射的性能问题 setAccessible方法简介

1.Method和Filed,Constructor对象都有setAccessible()方法

setAccessible作用是启动和禁用访问安全检查的开关

2.参数值为true则是反射的对象在使用时应该取消java语言访问检查

  • 提高反射的效率,如果代码中必须使用反射,而该句代码需要平凡被调用那么请设置为true。

  • ​ 使得原本无法访问的私有成员变量也可以访问

3.参数值为false则指示反射的对象应该实施java语言访问检查
6.1.java性能问题测试

package com.gun.classforName;

import java.lang.reflect.Method;

/**
 * 反射性能测试
 * @author jkl
 *
 */
public class TestProper {

	public static void main(String[] args) throws Exception {
		test01();
		test02();
		test03();
		test04();
	}
	//传统new方式
	public static void test01(){
		Users u = new Users();
		long start = System.currentTimeMillis();
		int count =2000000000;//十亿次
		for(int i = 0 ;i <count ;i++  ){
			u.getName();
		}
		long end =System.currentTimeMillis();
		System.out.println("new需要:"+(end - start)+"毫秒");
		
	}
	public static void test02() throws Exception{
		Class cl= 	Class.forName("com.gun.classforName.Users");
		Users u =(Users)cl.newInstance();
		Method method = cl.getDeclaredMethod("getName", null);
		method.setAccessible(true);
		long start = System.currentTimeMillis();
		int count =1000000000;//十亿次
		for(int i = 0 ;i <count ;i++  ){
				method.invoke(u, null);
		}
		long end =System.currentTimeMillis();
		System.out.println("newInstance创建对象需要:"+(end - start)+"毫秒");
	}
	
	public static void test03() throws Exception{
		Users u = new Users();
		Class cl = u.getClass();
		Method method= cl.getDeclaredMethod("getName", null);
		long start = System.currentTimeMillis();
		int count =1000000000;//十亿次
		for(int i = 0 ;i <count ;i++  ){
			method.invoke(u, null);
		}
		long end =System.currentTimeMillis();
		System.out.println("Accessible默认为false需要:"+(end - start)+"毫秒");
	}
	
	public static void test04() throws Exception{
		Users u = new Users();
		Class cl = u.getClass();
		Method method= cl.getDeclaredMethod("getName", null);
		method.setAccessible(true);
		long start = System.currentTimeMillis();
		int count =1000000000;//十亿次
		for(int i = 0 ;i <count ;i++  ){
			method.invoke(u, null);
		}
		long end =System.currentTimeMillis();
		System.out.println("Accessible设置为true需要:"+(end - start)+"毫秒");
	}
}

输出结果

总结:使用new的方式性能高,使用反射的方式性能相对较低。使用反射方式开启setAccessible设置为true

可以提高性能。但还是没有使用new的方式高。

实际开发中应用场景:当一个业务类的接口特别复杂,如果我们想在不改变原理类的情况增加业务方法时,

可以采用java的反射机制来完成,也可以采用java的动态代理来完成(动态代理其实也是基于java的反射机制来完成的)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/743167.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-29
下一篇 2022-04-29

发表评论

登录后才能评论

评论列表(0条)

保存