静态导入,枚举,反射,内省

本文深入探讨Java反射机制的基础知识及其应用场景,包括类的加载过程、反射API介绍、反射在框架中的应用等内容。同时,文章还介绍了JavaBean规范与内省机制,展示了如何利用反射和内省操作JavaBean。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

静态导入:
import语句可以导入一个类或某个包中的所有类
import static语句导入一个类中的某个静态方法或所有静态方法
语法举例:
import static java.lang.Math.sin;
import static java.lang.Math.*;

基本数据类型的自动拆卸与装箱:
自动装箱:
Integer num1 = 12;
自动拆箱:
System.out.println(num1 + 12);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12;  这块相等,<=127都是真的
System.out.println(num1 == num2);

Integer num3 = 129;                这块不相等,因为是对象
Integer num4 = 129;
System.out.println(num3 == num4);
//因为当数值在byte范围内容,对新特性,如果该数值已经存在,则不会再开辟新的空间
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12)  ;   这块的道理同上
System.out.println(num5 == num6);

枚举:
 枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,
 否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,
 普通变量的方式在开发阶段无法实现这一目标。
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,
例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。

public class EnumTest 
{
	public static void main(String[] args)
	{
		WeekDay weekDay2 =WeekDay.FRI;//获取枚举常量
		//调用枚举类获取枚举值(枚举常量),枚举里的枚举值都是被public final static修饰。
		WeekDay weekDay3= WeekDay.SUN;
		WeekDay weekDay4= WeekDay.valueOf("WED");//也可以获取枚举常量
		System.out.println(weekDay3);//枚举值的名字
		System.out.println(weekDay2.name());//枚举值的名字
		System.out.println(weekDay2.ordinal());	//枚举值的位置
		//返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
		System.out.println(WeekDay.valueOf("SUN").toString());
		//valueOf返回带指定名称的指定枚举类型的枚举常量。
		System.out.println(WeekDay.values().length);
	}
	public enum WeekDay//枚举就是一个类,在这个例子中定义方法是内部类了
	{
		//这些枚举值都是public static final的,也就是我们经常所定义的常量方式,
		//因此枚举类中的枚举值最好全部大写。
		SUN(1),MON(),TUE,WED,THI,FRI,SAT;//必须第一排,这些都是对象
		private WeekDay(){System.out.println("first");}//构造方法,必须私有
		//构造方法只是在构造枚举值的时候被调用。
		private WeekDay(int day){System.out.println("second");}//必须私有
		
	}
}

带方法的枚举
 定义枚举TrafficLamp
 实现普通的next方法
 实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,
 这些子类采用类似内部类的方式进行定义。
 增加上表示时间的构造方法

public enum TrafficLamp{
		RED(30){
			public  TrafficLamp nextLamp(){
				return GREEN;
			}
		},
		GREEN(45){
			public  TrafficLamp nextLamp(){
				return YELLOW;
			}			
		},
		YELLOW(5){
			public  TrafficLamp nextLamp(){
				return RED;
			}			
		};
		public abstract TrafficLamp nextLamp();
		private int time;
		private TrafficLamp(int time){this.time = time;}
	}
}



 

反射:
反射的基石-Class 类
Class 类:类对应的字节码。对应各个类在内存中的字节码(一个类在虚拟机中通常只有一份字节码),
  例如,Person类的字节码,ArrayList类的字节码,等等。
  一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
  不同的类的字节码是不同的,所以它们在内存中的内容是不同的,
  它们的类型是Class类型。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
如何得到各个字节码自己对应的实例对象( Class类型)
类名.class,例如,Class cls1 = Person.class
对象.getClass(),例如,Class cls2 = new Date().getClass()
Class.forName("类名"),例如,Class cls3 = Class.forName("java.util.Date");

获取这个Class对象,有三种方式:

1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。

2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类

 前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。

3:使用的Class类中的方法,静态的forName方法

 指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可

 

上面三种方法得到的是同一份字节码
//两种返回字节码方式,一种是内存中已经存在的字节码,直接获取。
//另一种是内存中不存在那份字节码,需要先用类加载器加载,在获取。
数组类型的Class实例对象
Class.isArray()

八个基本类型加viod:对应Class对象
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)
和关键字 void 也表示为 Class 对象。

方法:
public boolean isPrimitive()判定指定的 Class 对象是否表示一个基本类型。
Integer.TYPE是int类型。
别外:
Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE

定义:反射就是把Java类中的各种成分映射成相应的java类。
 具体获取:java类的Class类显然要提供一系列的方法,来获得其中的变量,
 方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,
 它们是Field、Method、Contructor、Package等等。

Constructor (构造方法类):

Class里的方法:
static Class<?> forName(String className) 
          返回与带有给定字符串名的类或接口相关联的 Class 对象。 
Constructor<T> getConstructor(Class<?>... parameterTypes) //可变参数
      返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Class<?>[] getDeclaredClasses() 
返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。 
 T newInstance() 
          创建此 Class 对象所表示的类的一个新实例。 

得到某个类所有的构造方法:
例子:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: 	   
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

得到构造方法后:
Constructor 的方法
 T newInstance(Object... initargs) 
	使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,
	并用指定的初始化参数初始化该实例。
传入new StringBuffer("abc")对象,创建对应构造方法的对象
String str =(String)constructor.newInstance(new StringBuffer("abc"));

创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
	//调用获得的方法时要用到上面相同类型的实例对象

Class.newInstance()方法:
(以前获取过constructor的构造方法,可以直接调用Class里的方法创建对象,不用再获取构造方法)
例子:String obj = (String)Class.forName("java.lang.String").newInstance();

java反射对象和new 出来的对象有什么区别?
反射对象是直到程序运行期间才知道类的名字的实例,这时才获取对象的成员,并设置属性。此时要用到类的全路径 ,用类的全路径来加载类,并返回该类的对象。以后就可以遍历类中的各个方法,各个属性。
   new是给类直接在内存中创建一个实例,并且可以直接初始化等。不需要类的全路径。
反射会导致程序性能严重下降。


Field 类:(类里的成员变量)

public class ReflectPoint//要运行请将每个public类分别放在不同文本中,然后导包
{	
	private int x;
	public int y;
	public String str1 = "ball";
	public String str2 = "basketball";
	public String str3 = "itcast";	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
public class ReflectTest1 {
	public static void main(String[] args) throws Exception 
	{	
		ReflectPoint pt1 = new ReflectPoint(3,5);
		Field fieldY = pt1.getClass().getField("y");
		//fieldY的值是多少?是5,错!fieldY不是对象身上的变量,
		//而是类上,要用它去取某个对象上对应的值,取值看下面
		
		//暴力反射。获取私有成员变量
		Field fieldX =pt1.getClass().getDeclaredField("x");//获取私有成员的字节码,但运行使用不了。
		fieldX.setAccessible(true);//设置为true,就可以访问了。
		fieldX.get(pt1);//取到了强制使用
		
		//将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
		changeStringValue(pt1);
		private static void changeStringValue(Object obj) throws Exception 
		{
			Field[] fields = obj.getClass().getFields();
			for(Field field : fields)
			{
			//if(field.getType().equals(String.class)){
			//为什么用==,因为字节码为同一份。以后比较字节码都用==
				if(field.getType() == String.class){
				String oldValue = (String)field.get(obj);
				String newValue = oldValue.replace('b', 'a');
				field.set(obj, newValue);	
			}
		}
	}
}


Method :(类里的方法)

Class里的方法:	Method getMethod(String name, Class<?>... parameterTypes) 
				返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的
				指定公共成员方法。
Method里的方法:Object invoke(Object obj, Object... args) 
				对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 
通常方式: String str ="abc";
			str.charAt(1);
反射方法:String str ="abc";
//获取成员方法,反射获取方法,再使用对象来调用方法。如果str1位置为null,及可以知道是静态方法。
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));//1.5getMethod(String name, Class<?>... parameterTypes) 
/*methodCharAt.invoke(str1, new Objeck[](2));1.4语法
理解诶1.4语法 new Objeck[](new String("abc"),1);数组里面装数组及其他
new Object[](new String("abc"),1)*/

用反射方式执行某个类中的main方法: 

class TestArguments//要运行请将每个public类分别放在不同文本中,然后导包
{
	public static void main(String[] args){
		for(String arg : args){
			System.out.println(arg);
		}
	}
}
public class ReflectTest2 {
	public static void main(String[] args) throws Exception 
	{
	//写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
	//TestArguments.main(new String[]{"111","222","333"});//通常方法
	String startingClassName = args[0];//运行时需要传递类名,即java ReflectTest2 包名.需要运行的类名。	
	Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
	mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//1.4特性,		
	//mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
	}
}

 

数组反射:

Class里的方法:
Class<? super T> getSuperclass() 
       返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
		如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象。
	
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
注意:基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;
	非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
		int [] a1 = new int[]{1,2,3};//父类为Object
		int [] a2 = new int[4];
		int[][] a3 = new int[2][3];
	a1与a2的字节码相同,a1与二位数组a2的字节码不相同。
		String [] a4 =new String[]{"a","b","c"};//父类为Object
	a1与a4的字节码不相同。因为类型不同
结合Object即看上面说明理解下面:
//基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;	
	Object Obj1 =a1;//int [] a1 = new int[]{1,2,3}
	Object[] Obj3 =a3;//int[][] a3 = new int[2][3];
//非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
	Object Obj2 =a4;//String [] a4 =new String[3];
	Object[] Obj4 =a4;//String [] a4 =new String[3];

Arrays 方法:
	asList:将数组转换为List集合。1.4与1.5的变化。可变参数。先找1.4,在找1.5
	Sop(a1);//[@4AFDSF
	Sop(a4);//String:@4AFDSF
	Sop(Arrays.asList(a1));//[[@4AFDSF]
	Sop(Arrays.asList(a4));//打印出来a,b,c

数组反射例子:

private static void printObject(Object obj) 
{
		Class clazz = obj.getClass();//得到传入的实例自己码
		if(clazz.isArray())//如果是数组
		{
			int len = Array.getLength(obj);//获取数组的长度
			for(int i=0;i<len;i++)//打印数组里的元素
			{
				System.out.println(Array.get(obj, i));
			}
		}else
		{
			System.out.println(obj);
		}		
}

 

内存泄露:没被使用,但对象还在内存中占有内存。没关流也会内存泄露,调用的底层资源没关。

public class ReflectPoint
{
	public static void main(String[] args) throws Exception 
	{
		Collection col =new HashSet()
		ReflectPoint pt1 =new ReflectPoint(3,3);//内存地址不同
		ReflectPoint pt2 =new ReflectPoint(5,5);
		col.add(pt1);
		col.add(pt2);
		col.add(pt1);
		//添加进去2个元素
		pt1.y =7;//修改后,hashCode值改变了,跑到另一个区域里了
		col.remove(pt1);.//删除的时候,还是去找原来的位置。所以没删除掉
		System.out.println(col.size());//删除了但打印还是2,所以没删除掉
	}
}

反射的作用:实现框架功能
框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?
 我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,
 所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。

框架与工具类:
 框架是框架调用类。
 工具是类调用工具。

 

内省:
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,
且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,
这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。
这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,
则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?
JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。(ter为去属性)

如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?
如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?
//注意:去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
 Age-如果第二个字母是小的,则把第一个字母变成小的-age
 setId()的属性名  id
 isLast()的属性名  last
 setCPU的属性名是什么?  CPU
    getUPS的属性名是什么?  UPS

实例:

public class ReflectPoint //JavaBean,特殊类
{
	private int x;
	private int y;		
	public ReflectPoint(int x, int y) 
	{
		super();
		this.x = x;
		this.y = y;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
}
//导包
public class IntroSpectorTest //要运行请将每个public类分别放在不同文本中,然后导包
{
	public static void main(String[] args) throws Exception
	{
		ReflectPoint pt = new ReflectPoint(3,5);
		//反射的方式:"x"-->"X"-->"getX"-->MethodGetX-->
		
		//下面是内省的方式:"x"-->MethodGetX
		String propertyName= "x";//设置属性名
		//属性描述符:PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。
		//PropertyDescriptor(String propertyName, Class<?> beanClass) 
        // 通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。
		PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt.getClass());
		//Method getReadMethod() 获得应该用于读取属性值的方法。      
		Methid methidGetX = pd.getReadMethid();
		//Object invoke(Object obj, Object... args) 
        //对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
		Object retVal = methidGetX.invoke(pt);
		System.out.println(retVal);
		
		//获得应该用于写入属性值的方法。
		Methid methidGetX = pd.getWriteMethid();
		//Object invoke(Object obj, Object... args) 
        // 对带有指定参数的指定对象调用由此 Method 对象表示的底层方 
		methidGetX.invoke(pt,7);
		//打印修改后的x值
		System.out.println(pt.getX());
		
	//下面是抽取成方法:
	private static void setProperties(Object pt, String propertyName,Object value) 
	throws IntrospectionException,IllegalAccessException, InvocationTargetException 
	{
		PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt.getClass());
		Method methodSetX = pd2.getWriteMethod();
		methodSetX.invoke(pt,value);
	}
	private static Object getProperty(Object pt, String propertyName)
	throws IntrospectionException, IllegalAccessException,InvocationTargetException 
	{
		PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt.getClass());
		Method methodGetX = pd.getReadMethod();
		Object retVal = methodGetX.invoke(pt);
		/* 从所有属性中获取自己需要的属性(张老师最早使用的代码,有些麻烦,没上面好)
		BeanInfo beanInfo =  Introspector.getBeanInfo(pt1.getClass());
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		Object retVal = null;
		for(PropertyDescriptor pd : pds){
			if(pd.getName().equals(propertyName))
			{
				Method methodGetX = pd.getReadMethod();
				retVal = methodGetX.invoke(pt1);
				break;
			}
		}*/
		return retVal;
	}
}

Beanutils工具包:和上面的抽取的方法差不多,就是别人已经定义好了的内省方法。
     只需要导入别人的jar包使用就可以了



            


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值