java反射和枚举(Reflect & Enum)


 

一、反射

1、历史:

是从java1.2开始引入的一个概念。

2、精辟概念:
反射就是把java类中的各种成分映射成相应的java类。(反射就是用来获取字节码文件内容的!)

3、现实例子:
一个java类中用一个Class类的对象来表示,一个类中的组成部分有“成员变量“、”方法“、”构造方法“、”包“等等信息也用一个个的java类来表示。就像汽车是一个类,汽车中的发动
机,变速等等也是一个个的类。表示java类的Class类显然要提供一系列的方法来获得其中的”变量“、”方法“、”构造方法“、”修饰符“、”包“等信息,这些 信息就是用相应类的实例对象来表示,它们是Field、Method、Constructor、Package。反射比较占用性能,反射会导致程序性能严重下降!


4、注意:

反射其实也是一种编程思想,自己可以使用各种获取字节码文件的类来获取相应的字节码中的文件内容。比较的时候,字节码用“等号”比!


反射小程序:

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


public class ReflectDemo {

	/**
	 * @param args
	 * @throws ClassNotFoundException 
	 * @throws NoSuchMethodException 
	 * @throws SecurityException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws IllegalArgumentException 
	 * @throws NoSuchFieldException 
	 */
	public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, 

InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
		// TODO Auto-generated method stub
		String ss="nihao";
		Class c1=ss.getClass();
		Class c2=String.class;
		Class c3=Class.forName("java.lang.String");
		System.out.println(c3+"\r\n"+c2+"\r\n"+c1);//结果是一模一样的!
		System.out.println(c2.isPrimitive());	//说明了String不是基本的数据类型,虽然经常用到。
		System.out.println(int.class.isPrimitive());//说明了int是一个基本的数据类型。
		System.out.println(int.class==Integer.class);//两个不相同的数据类型
		System.out.println(int.class==Integer.TYPE);//TYPE代表的是这个包装类型所包装的基本类型的字节码,所以返回的是true
		System.out.println(int[].class.isPrimitive());//数组不是基本的数据类型
		System.out.println(int[].class.isArray());//判断这种数据类型是不是array
		
		Constructor con1=String.class.getConstructor(StringBuffer.class);
		String ss1=(String)con1.newInstance(new StringBuffer("kkk"));
		System.out.println(ss1);
		System.out.println(ss1.charAt(1));
		//上面是:得到构造方法再用构造方法new一个Instance最后得出,这就是反射。
		
		ChengYuanBianLiangFanShe cy=new ChengYuanBianLiangFanShe(3, 4);
		Field fy=cy.getClass().getField("y");//getField方法返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段
		System.out.println(fy.get(cy));//Field类中get方法可以获取相应对象中的数值。
		
//		Field fx=cy.getClass().getField("x");	
//		System.out.println(fx.get(cy));	//这里将会报错,因为在文件“ChengYuanBianLiangFanShe”的字节码中x是被私有化private的了
		
		Field fx=cy.getClass().getDeclaredField("x");	//这里的意思就是获取所有的参数“Declared”就是公告的意思。
		fx.setAccessible(true);//这里还得设置一下是否可以访问。
		System.out.println(fx.get(cy));//这个称为“暴力反射”!
		
		//调用下面的changeStringValue方法
		changeStringValue(cy);
		System.out.println(cy);
		
		//ss1.charAt(1);
		Method methodCharAt=String.class.getMethod("charAt",int.class);//第一个参数是String类中的方法的名字,后面的int类型的字节码文件
		System.out.println(methodCharAt.invoke(ss1,1));//对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
		
	}
	private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException{
		Field[] fields=obj.getClass().getFields();//返回的是一个字段类型的数组
		for(Field field:fields){	//对Field类型的数组中的字段进行一一遍历
			if(field.getType()==String.class){	//判断如果上面传值进来的类创建的对象的字段的声明类型是String的话,那么就进入下面的替换操作
				String oldValue=(String) field.get(obj);
				String newValue=oldValue.replace('b','.');
				field.set(obj, newValue);
			}
		}
	}

}

Class类:
Person p1=new Person();
Person p2=new Person();

Date

Math

Class c1=Date.class//字节码1
Class c2=Person.class//字节码2

p1.getClass();

上面用到了Person、Date、Math三个类,就会有三份字节码文件。每一份字节码就是Class类的一个实例对象。Date.class表示Date这个类在内存中的那份字节码,这份字节码就是一个对象,这个对象的类型就是Class类型的。p1就是字节码搞出来的对象,调用getClass就可以得到所对应的class。


反射面试题(Class.forName的作用):
Class.forName("java.lang.String")返回字节码,返回的方式有两种:1、这份字节码曾经被加载过,已经待在java虚拟机内存中了,直接返回。2、java虚拟机中还没有这份字节码,则用类
加载器去加载,将加载进来的字节码放到虚拟机内存中,以后要得到这份字节码就不用加载了。

得到字节码的方式有三种:
1、类名.class。例如:Person.class
2、对象.getClass。例如:new Date().getClass();
3、Class.forName("类名")。例如:Class.forName("java.util.Date");-----反射的时候用的多。
这三种使用的频率都很高,在使用“反射”的时候使用的是“第三种”。

java中有八个基本数据类型。其中String不是基本类型,虽然自己经常用到。但是String不是一个基本类型的字节码!

九个预定义Class实例对象:

1、 boolean、byte、char、short、int、long、float 和double,还有void,

2、 对应的类型Boolean.TYPE,Character.TYPE,Byte.TYPE,Short.TYPE,Integer.TYPE,

Long.TYPE,Float.TYPE,Double.TYPE,Void.TYPE

 

 

小代码(说明了很多问题):

public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		String ss="nihao";
		Class c1=ss.getClass();
		Class c2=String.class;
		Class c3=Class.forName("java.lang.String");
		System.out.println(c3+"\r\n"+c2+"\r\n"+c1);//结果是一模一样的!
		System.out.println(c2.isPrimitive());	//说明了String不是基本的数据类型,虽然经常用到。
		System.out.println(int.class.isPrimitive());//说明了int是一个基本的数据类型。
		System.out.println(int.class==Integer.class);//两个不相同的数据类型
		System.out.println(int.class==Integer.TYPE);//TYPE代表的是这个包装类型所包装的基本类型的字节码,所以返回的是true
		System.out.println(int[].class.isPrimitive());//数组不是基本的数据类型
		System.out.println(int[].class.isArray());//判断这种数据类型是不是array
	}
}

数组类型的Class实例对象:Class.iaArray();
总之,只要是在源程序中出现的类型,都有各自的Class实例对象。例如,int[],void....都是可以的!

创建反射的时候经常用到的规则是:class-->constructor-->new object


constructor类:
编译器只看变量的定义,不看代码的执行!这个类是用来获取每个java字节码中的“构造方法”的内容的!

 

Field类:
这个类的意思就是获取一个类文件中的相应的字段内容,比方说定义的int型成员变量的数值是多少,String类型的字符串内容是什么。

 

Method类:

就是将一个字节文件中的与方法有关的内容进行操作。

 

JDK一些特性的增加(小普及):
1、泛型是1.5之后出现的新特性
2、静态导入是1.5之后出现的特性
3、自动装箱和拆箱是1.5之后出现的
4、享元设计模式是1.5之后出现的
5、枚举是1.5种增加的一个新特性
6、可变参数是1.5的新特性(所以Method类一个方法invoke中的多个参数的思想就发生了变化:jdk1.5--invoke(Object obj,Object...args)、jdk1.4--invoke(Object obj,Object[] args))


 

可变参数小例子:

public class KeBianCanShu {

	/**
	 * @param args
	 */
	public static void add(int x,int ... args){
		int sum=0;
		for(int i=0;i<args.length;i++){
			sum=sum+args[i];
		}
		System.out.println(sum);
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		add(1,2,3,4,5);
	}

}

枚举概念:
就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。

 

枚举步骤(定义一个weekday的类来模拟枚举功能):
1、私有的构造方法。
2、每个元素分别用一个公有的静态成员变量。
3、可以有若干共有方法或抽象方法,例如,要提供nextDay方法必须是抽象的,采用抽象方法定义nextDay就将大量的if。。。else语句转移成了一个个独立的类。

 

枚举的构造方法:
1、枚举构造方法必须定义在“对象”的后面!
2、枚举的构造方法必须是private私有的!
3、“默认构造函数“和”自定义构造函数“的不同如下:

public class EnumDemoToo {
	public static void out(Object obj){
		System.out.println(obj);
	}
	public EnumDemoToo(){
		out("nihao");
	}
	public static void main(String[] args) {	//枚举的基本方法
		WeekDay w = WeekDay.星期一;
//		out(w);
//		out(w.name());
//		out(w.ordinal());
//		out(w.valueOf("星期五").toString());
//		out(w.values().length);
		
	}
	public enum WeekDay{	//一个枚举类型的内部类
		星期天("SUN"),星期一("MON"),星期二("TUE"),星期三("THI"),星期四("FRI"),星期五("FRI"),星期六("SAT");
		private WeekDay(){
			out("默认构造函数");
		}	//默认无参数构造方法
		private WeekDay(String name){
			out("自定义构造函数");
		}	//有参数构造方法
	}
}

 

4、枚举只有一个成员时间,就可以作为一种单例的模式了。

5、例子如下:

public class EnumDemo8 {
	public static void main(String[] args) {
		Light l=Light.RED;
	}
	public enum Light{	//枚举内部类
		RED(30){
			public Light nextLamp(){//复写抽象方法
				System.out.println("我是红灯");
				return GREEN;
			}
		},
		GREEN(40){
			public Light nextLamp(){
				System.out.println("我是绿灯");
				return YELLOW;
			}
		},
		YELLOW(50){
			public Light nextLamp(){
				System.out.println("我是黄灯");
				return RED;
			}
		};
		public abstract Light nextLamp();//抽象方法没有任何内容
		private int time;
		private Light(int time){	//私有化构造方法
			this.time=time;
		}
	}
}

 

享元设计模式(小扩展):
就是说将很多模式一样的内容创建一个对象来管理,比方说文件夹,很多文件夹,如果一个文件夹创建一个对象,那么实在是太多了,内存受不了。
比方说:

Integer i1=13;

Integer i2=13;
那么i1==i2就返回的是true,这就是享元模式,可以用一片内存的就不用两片内存。

 

自动装箱和拆箱(小扩展):
Integer i=3;
就是将一个int类型数据自动装箱成Integer类型的数据(装箱过程)。
System.out.println(i+12);
就是将Integer类型数据拆成了int类型的数据(拆箱过程)。


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Java反射机制来动态生成枚举类。下面是一个示例代码: ```java import java.lang.reflect.Constructor; public class EnumReflectionExample { public static void main(String[] args) throws Exception { // 枚举类名 String enumClassName = "com.example.EnumExample"; // 枚举常量名称 String constantName = "VALUE3"; // 新的枚举常量值 Object[] newEnumConstants = { "New Value 3" }; // 获取原始枚举类对象 Class<?> enumClass = Class.forName(enumClassName); Object[] enumConstants = enumClass.getEnumConstants(); // 创建新的枚举类数组,长度比原始数组多1 Object[] newEnumValues = new Object[enumConstants.length + 1]; System.arraycopy(enumConstants, 0, newEnumValues, 0, enumConstants.length); // 创建新的枚举常量对象 Class<?>[] paramTypes = { String.class, int.class }; Constructor<?> constructor = enumClass.getDeclaredConstructor(paramTypes); constructor.setAccessible(true); Object newEnumConstant = constructor.newInstance(constantName, newEnumConstants.length); newEnumValues[newEnumConstants.length] = newEnumConstant; // 更新枚举常量值 Field valuesField = enumClass.getDeclaredField("$VALUES"); valuesField.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(valuesField, valuesField.getModifiers() &amp; ~Modifier.FINAL); valuesField.set(null, newEnumValues); // 输出更新后的枚举常量值 for (Object constant : enumClass.getEnumConstants()) { System.out.println(constant); } } } ``` 这个示例中,我们首先通过Class.forName()方法获取到原始的枚举类对象。然后,我们创建一个新的枚举常量数组,将原始的枚举常量值复制到新数组中。接下来,我们使用反射来创建一个新的枚举常量对象,并将其添加到新的枚举常量数组中。最后,我们使用反射来更新枚举类的私有静态字段"$VALUES"的值,将其替换为新的枚举常量数组。 请注意,这种动态生成枚举类的方法并不是Java语言本身提供的,而是使用了反射机制来实现的。因此,在使用时需要谨慎考虑其适用性和可能带来的潜在问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值