java基础加强-枚举和反射

1.枚举:

目的:让某个类型的变量取值只能为若干个固定值中的一个,否则,编译器会报错;

作用:可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。

练习:用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。

步骤:  1.私有构造函数,防止外部随意创建对象;

    2.每个元素分别用一个公有的静态成员变量表示;

    3.可以有若干公有方法或抽象方法,例如,要提供nextDay()方法必须是抽象的,因为具体的星期中的第一天是星期几我们是不知道的。

			class WeekDayDemo{
				public static void main(String[] args) {
					WeekDay weekDay=WeekDay.MON;
					System.out.println(weekDay.nextDay());
				}
			}
				//将nextDay方法分别由其子类去覆写它中的内容
				abstract class WeekDay{
				//在这里假设一周只有两天,周一和周日,循环。
				public final static WeekDay SUN=new WeekDay(){
					@Override
					public WeekDay nextDay() {
						return MON;
					}
				};
				public final static WeekDay MON=new WeekDay(){
					@Override
					public WeekDay nextDay() {
						return SUN;
					}
				};
				private WeekDay(){}
				public String toString(){
					return this==SUN?"SUN":"MON";
				}
				public abstract WeekDay nextDay();
				/*public WeekDay nextDay(){
					if(this==SUN){
						return MON;
					}else{
						return SUN;
					}
				}	*/
				//将许多的if else语句转成了一个个独立的类,有多少个子类,就转成多少个类。
			}
枚举的基本应用:定义一个WeekDay枚举,包括一些基本方法;

	class WeekDay{
		public static void main(String[] args) {
			WeekDay weekDay1=WeekDay.FRI;
			System.out.println(weekDay1);//这里已经覆写过toString方法,可以直接输出对象内容;
			System.out.println(weekDay1.name());
			System.out.println(weekDay1.ordinal());//获取到枚举的序数
			System.out.println(WeekDay.valueOf("SUN".toString()));//将参数列表中的参数变为对象
			System.out.println(WeekDay.values().length);//values方法将其变为数组
		}
		//在类中定义一个枚举
		public enum WeekDay{
			SUN,MON,TUE,WED,THD,FRI,SAR;
		}
	}

总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,

实现带构造函数的枚举:

注意:枚举类Enum中的构造函数 protected  Enum(String name,int ordinal),程序员无法调用此构造方法,只有它的子类才能使用,用于由响应枚 举类型声明的编译器发出的代码。

在上面代码基础之上:

		public enum WeekDay{
			SUN(1),MON,TUE,WED,THD,FRI,SAR;
			private WeekDay(){
				System.out.println("first");
			}
			private WeekDay(int day){
				System.out.println("second");
			}
			//在这里,有两个构造函数,如果想知道对象在静态初始化后,调用的是哪个构造函数,可以通过看对象是否带有参数。
				// 如这里的SUN就调用的有参构造函数。
		}
	}

实现带有抽象方法的枚举:

		class EnumTrafficLamp{
			public static void main(String[] args){
				TrafficLamp tl=TrafficLamp.GREEN;
				System.out.println(tl.nextLamp());
			}
			public enum TrafficLamp {
			//因TrafficLamp是抽象的,所以创建它的三个子类RED,GREEN,YELLOW来覆写它的抽象方法
			//
				RED(20l) {
					@Override
					public TrafficLamp nextLamp() {
						return GREEN;
					}
				},GREEN(32l) {
					@Override
					public TrafficLamp nextLamp() {
						return YELLOW;
					}
				},YELLOW(10l){
					@Override
					public TrafficLamp nextLamp() {
						return RED;
					}
				};
				public abstract TrafficLamp nextLamp();
				//定义时间,作为交通灯的等待时间
				private long time;
				TrafficLamp(long time){
					this.time=time;
				}
			}
		}

          注意:枚举中只有一个成员变量时,可以作为一种单例的实现方式。
2.反射(JDK1.2就有的特性)

1.反射的基石:Class,它是一个类,java中各个类都是类,描述这些类的类就是Class.即Class是描述java中类的类;

基本方法应用:它没有公有的构造函数,不能通过new来创建实例对象,而是根据加载进内存的.class文件,如:

			Person p1=new Person();
			Person p2=new Person();
			Class c1=Person.class;//Person类的的字节码就是Class类的一个实例。
			p1.getClass();//获得对象所属的类的字节码
			Class.forName("java.lang.String");
		//得到类的字节码,返回方式两种:如果已加载进内存,则不需再加载直接返回;如果虚拟机中没有此字节码,类加载区需加载,缓存在JVM中,以后不用再加载;
总结:得到各个类实例对象的三种方式:

1.类名.class;如System.class

2.对象.getClass();如new Person().getClass();

3.Class.forName("类名");如Class.forName("java.lang.Date");

九个预定义的Class对象:八个基本数据类型加 void;

Class中有一个方法isPrimitive(),返回布尔型,判断指定的Class实例对象是否表示一个基本数据类型。

			Class cl=void.class;	//表示void的Class实例对象	
              			 String s="abc";
				Class c1=s.getClass();
				Class c2=String.class;
				Class c3=Class.forName("java.lang.String");//这里会抛异常,因为可能类名写错以致找不到类。
				System.out.println(c1==c2);//true
				System.out.println(c1==c3);//true
				System.out.println(c1.isPrimitive());//false,因为String类不是基本类型。
				System.out.println(int.class==Integer.TYPE);//true
				System.out.println(int[].class.isArray());//数组的Class实例对象,true
总结:只要是在源程序中的类型,都有自己的Class实例对象。

2.反射:把java类中的各种成分映射成相应的java类。

动态的获取类中的内容,自动去找相应的类,并自动加载这个类创建该类的实例对象;在类中,将类中的Filed,Method, Constructor,Package都封装成各自的类。

好处:提高了程序的扩展性(多态也能提高程序的扩展性,但要自己new对象,而反射自动new对象,不用手动操作)。

		class Class{
			//Class类中的属性
			Field field;//字段
			Constructor constructor;//构造器
			Method method;//方法
			
			//Class类中的方法
			Field getField(name);//获取单个字段
			Field[] getFields;//获取所有所有字段
			Constructor getConstructor();//获取单个构造函数
			Constructor[] getConstructors();//获取所有构造函数
			Method getMethod();//获取方法
			Method[] getMethods();
		}
反射首先要获取到字节码文件Class的对象,有三种方式:

方式一:通过对象的getClass()方法;如Class cla=对象.getClass();该种方式不利于扩展,因其需要使用该类对象,并还需创建该类对象;

方式二:任何一个数据类型,都对应着一个类的描述,都可以获取到对应的Class对象,而且通过数据类型的一个静态属性.class完成,但还是要用到对象,对扩展性仍然不好;

方式三:通过Class类中的静态方法forName(String name)方法,根据指定的字符串名称(全类名),可以获取到对应的字节码文件;这种方式不会用到对象,扩展性最好;

如 Class cla=Class.forName("java.lang.String");

反射第二步是创建字节码文件的对象,即Class具体描述的食物实例;

			Class cla=Class.forName("java.lang.Object);
			Object obj=cla.newInstance();
			//这两句话相当于下面一句话;
			Object obj=new Object();

Object obj=new Object();这句话的原理:

1.加载了java.lang.Object.class文件进内存;

2.将该文件封装成Class对象;

3.通过new关键字在堆内存中分配空间;

4.调用空参数的构造函数堆该类对象进行初始化。

Class cla=Class.forName("java.lang.Object:);

Object obj=cla.newInstance();原理:

1.查找指定的字节码文件;

2.将其加载进内存,并封装成Class对象;

3.通过newInstance创建实例;其实就是在该方法内部通过new关键字创建实例,并调用空参的构造方法对对象进行初始化。

注意:newInstance只能调用空参数的构造函数初始化对象。

反射第三步,创建字节码文件的带参数的对象,首先获取到这个构造函数的对象;代码体现如下:

			Class cla=Class.forName("java.lang.String");
				Object obj=cla.newInstance();
				//获取字节码文件对象对应的构造函数
				Constructor con=cla.getConstructor(String.class);
				//通过指定的构造函数new实例对象
				String s=(String)con.newInstance("acd");
				System.out.println(s);

反射四:通过反射调用对象中的属性,Field类;

		Class cla=Class.forName("cn.itcast.Person");
			Object obj=cla.newInstance();
			//age属性被私有
			Field field=cla.getDeclaredField("age");
			//需设置为可访问
			field.setAccessible(true);
			//对对象的age属性赋值
			field.set(obj, 22);
			field.get(obj);
			//toString方法被复写
			System.out.println(obj);
反射五:动态获取指定类中的方法;

getMethods():获取所有方法,包括从父类继承来的方法;

getDeclaredMethods():获取本类中所有权限的方法,不包括从父类继承来的方法。

获取public修饰的方法:

		Class cla=Class.forName("cn.itcast.Person");
		Object obj=cla.newInstance();
		Method method=cla.getMethod("方法名",null);
		//后面的这个参数是方法中的参数列表
		method.invoke(obj, null);
获取private修饰的方法:
		Class cla=Class.forName("cn.itcast.Person");
		Object obj=cla.newInstance();
		Method method=cla.getDeclaredMethod("方法名",null);
		method.setAccessible(true);
		method.invoke(obj, null);
获取static修饰的方法:
		Class cla=Class.forName("cn.itcast.Person");
		Object obj=cla.newInstance();
		Method method=cla.getMethod("方法名",null);
		method.invoke(null, null);//静态方法不需要对象
获取有参数的方法:

		Class cla=Class.forName("cn.itcast.Person");
		Object obj=cla.newInstance();
		Method method=cla.getMethod("Study",String.class,int.class);
		method.invoke(obj, "lisi",23);
练习:将任意一个对象中的String类型的成员变量所对应的字符串内容中的"b"改为"a"。

		public class Test {
			public static void main(String[] args) throws Exception {
				//获取字节码文件
				Class cla=Class.forName("Test.Ball");
				//创建字节码文件对象
				Object obj=cla.newInstance();
				//获取所有字段
				Field[] fields=obj.getClass().getDeclaredFields();
				for (Field field : fields) {
					field.setAccessible(true);
					//遍历所有符合条件的字段类型
					if (field.getType()==String.class) {
						String oldValue=(String)field.get(obj);
						String newValue=oldValue.replace('b','a');
						field.set(obj, newValue);
					}
				}
				System.out.println(obj);
		
			}

		}
		class Ball{
			private String s1="ball";
			private String s2="basketball";
			private String s3="pingpeng";
			public String toString(){
				return s1+"::"+s2+"::"+s3;
			}
		}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值