黑马程序员_java基础加强-枚举和反射

来源:互联网 发布:淘宝金牌卖家 编辑:程序博客网 时间:2024/06/10 09:50

---------- android培训、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(){@Overridepublic WeekDay nextDay() {return MON;}};public final static WeekDay MON=new WeekDay(){@Overridepublic 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) {@Overridepublic TrafficLamp nextLamp() {return GREEN;}},GREEN(32l) {@Overridepublic TrafficLamp nextLamp() {return YELLOW;}},YELLOW(10l){@Overridepublic 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;}}


---------- android培训、java培训、期待与您交流! ----------









原创粉丝点击