黑马程序员——Java基础:反射、JDK1.5新特性、正则表达式

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

 

注:前面所有总结都是通过毕向东老师所讲的视频所得。反射部分由张孝祥老师所讲。

一、反射

反射 :是指程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。反射就是得到元数据的行为。

反射的基础是Class类:

    Class类:Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。

                    如:人——> Person    //Person类代表人,它的实例对象就是张三,李四这样一个个具体的人

                              Java类——> Class  //Class类代表Java类,它的各个实例对象对应各个类在内存中的字节码.。例如,Person类的字节码,ArrayList类的字节码等。

字节码:一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码。不同的类的字节码是不同的,所以它们在内存中的内容是不同的。

注:一个类在虚拟机中只有一份字节码。

得到字节码对应的实例对象的三种方法:

     1)类名.class //如:Class c1=Person.class;

     2)对象.getClass() //如:new Person().getClass()

     3)Class.forName(String 类名) //类名要是全限定名,如String的全限定名是java.lang.String

九个定义Class对象

黑马程序员——Java基础:反射、JDK1.5新特性、正则表达式_第1张图片

Class类中的方法:

     1.boolean isPrimitive() :判定指定的 Class 对象是否表示一个基本类型。

     2.boolean isArray() :判定此 Class 对象是否表示一个数组类型。

包装类和Void类的静态TYPE字段。包装类都有一个TYPE字段,用来表示其基本数据类型的字节码。

     如:Integer.TYPE == int.class ;          Integer.class ==int.class;      

数组类型的Class实例对象。如:Class clz = String[].class;

利用Class获取类的属性信息:

示例1:用示例的方式来演示Class类方法的使用

Class c=person.class;//类可以,接口也可以
c.getPackage()//得到包名
c.getName();//得到全限定名
c.getSimpleName()//得到类的简称
c.getSuperclass().getSimpleName()//先获取父类,再获取父类的简称
Class[] arr = c.getInterfaces();//得到接口
Class[] cl = c.getClasses();//获得public修饰的类。返回一个包含某些 Class 对象的数组,
//这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 (如果内部类前面没有加上public的话那么得不到!)

int i = c.getModifiers();//获得修饰符
System.out.println(i);//常量值1表示public
System.out.println(Modifier.toString(i));//直接打印出public

Constructor类用于描述类中的构造方法:

Constructor getConstructor(Class... parameterTypes) //返回该Class对象表示类的指定的public构造方法;
Constructor[] getConstructors()//返回该Class对象表示类的所有public构造方法;
Constructor getDeclaredConstructor(Class... parameterTypes)//返回该Class对象表示类的指定的构造方法,和访问权限无关;
Constructor[] getDeclaredConstructors() //返回该Class对象表示类的所有构造方法,和访问权限无关;
示例2:用示例的方式来演示Class类方法的使用

Class c=person.class;//类可以,接口也可以
Constructor c1 = c.getConstructor(String.class,int.class);//得到指定的构造器,也是必须public
Constructor[] con1 = c.getConstructors();//前面的修饰符必须是public才可以在这个方法下获取到
Constructor c2 = c.getDeclaredConstructor(String.class,int.class);//现在想获得不受public影响的,getDeclaredConstructor(),暴力反射
Constructor[] con2 = c.getDeclaredConstructors();//现在想获得不受public影响的,getDeclaredConstructors(),暴力反射

Method用于描述类中的方法:

Method getMethod(String name, Class ... parameterTypes) //返回该Class对象表示类和其父类的指定的public方法;
Method[] getMethods()//返回该Class对象表示类和其父类的所有public方法;
Method getDeclaredMethod(String name, Class... parameterTypes) //返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;
Method[] getDeclaredMethods()//获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;

示例3:用示例的方式来演示Class类方法的使用

Class c=person.class;//类可以,接口也可以
Method me = c.getMethod("main", String[].class);//获取指定的方法
Method[] m = c.getMethods();//获取所有的(包含父类的方法)public修饰的方法
Method me = c.getDeclaredMethod("show");//访问所有方法,不受访问权限影响
Method[] m = c.getDeclaredMethods();//访问所有方法,不受访问权限影响
/*注:Method[] getDeclaredMethods() :返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,
包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法,只可以对当前类有效*/

Field类用于描述类中的字段:

Field getField(String name, Class ... parameterTypes) //返回该Class对象表示类和其父类的指定的public字段;
Field[] getFields()//返回该Class对象表示类和其父类的所有public字段;
Field getDeclaredField(String name, Class... parameterTypes) //返回该Class对象表示类的指定的字段。和访问权限无关,但不包括继承的方法;
Field[] getDeclaredFields()//获得类所有的字段,包括公共、保护、默认(包)访问和私有方法,但不包括继承的字段;

示例4:用示例的方式来演示Class类方法的使用

Stu s = new Stu("刘昭", "男", 12);
Class c = Stu.class;
Field f = c.getField("name");//""里面是名称//特定字段
f.set(s,"章泽天");
f.get(s);//从哪个对象身上取!//此时显示章泽天

Field[] f = c.getFields()//只得到了public的字段
Field[] f = c.getDeclaredFields();//得到不受修饰符限定的字段,但是只对当前类有效

 Annotation[] a = c.getAnnotations();//注释  Annotation
 Deprecated d = c.getAnnotation(Deprecated.class);//特定注解

二、利用反射的方式创建对象

传统方式创建对象:new User( );

反射方式一:使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法。

                如:Class c = User.class;

                        Useru = c.newInstance();//前提条件:构造函数没有被私有化
反射方式二:使用Class对象获取指定的Constructor对象,再调用Constructor的newInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true,即设置setAccessible(boolean flag)方法,然后再调用Xxx x = getDeclaredXxxx();//才能得到私有的类字段

            步骤:1.获取该类的Class对象。

                        2.利用Class对象的getConstructor()方法来获取指定的构造方法。

                        3.申请访问。

                        4.调用ConstructornewInstance()方法创建对象。

               如:Class c=person.class;//获取本类的Class对象
                       Constructor con = c.getDeclaredConstructor(String.class);//获取指定的构造方法
                       con.setAccessible(true);//允许访问
                       Person p = con.newInstance(" ");//可以通过私有的受保护的构造方法创建对象

注:用反射的方式可以破解单例设计模式,但是对于枚举,反射依然没有办法创建对象。

三、使用反射方式调用方法

每个Method的对象对应一个具体的底层方法。获得Method对象后,程序可以使用Method里面的invoke方法来执行该底层方法。

Objectinvoke(Object obj,Object ... args):obj表示调用底层方法的对象,后面的args表示传递的实际参数。

如果底层方法是静态的,那么可以忽略指定的 obj参数。该参数可以为null。

如果底层方法所需的形参个数为 0,则所提供的 args数组长度可以为 0 null

传统方式:new Person().show("zhangsan");

反射方式:Class c=Person.class;

                    Method m=c.getMethod("show",String.class);//调用有参方法,参数是String类型

                    Object o=m.invoke(c.newInstance(),"zhangsan");

注:如果访问的是私有方法,需申请访问

        Method  m = c.getDeclaredMethod("privateshow");//调用无参方法

        m.setAccessible(true);//申请访问

        o=m.invoke(c.newInstance());

注:如果是静态方法,则不需要创建对象

        m.c.getMethod("staticshow");

        m.invoke(null);

 四、使用反射调用可变参数方法

把可变参数都当做是其对应的数组类型参数。如 :show(XX... is)作为show(XX[] is)调用;

1)若可变参数元素类型是引用类型:

      JDK内部接收到参数之后,会自动拆包取出参数再分配给该底层方法,为此我们需要把这个数组实参先包装成一个Object对象或把实际参数作为一个Object一维数组的元素再传递。

2)若可变参数元素类型是基本类型:

      JDK内部接收到参数之后,不会拆包,所以可以不必再封装.不过封装了也不会错.所以建议,不管基本类型还是引用类型都使用Object[]封装一层,保证无误.

如:Class c =VaryMethod.class;

        Method m = c.getMethod("show",int[].class);

        m.invoke(null,new int[]{1,2,3});

        m = c.getMethod("show",String[].class);//调用传String类型的可变参数show方法

        //m.invoke(null,new String[]{"A","B","C"});//ERROR

        m.invoke(null,(Object)new String[]{"A","B","C"});//YES,强转为Object类型

        m.invoke(null,new Object[]{new String[]{"A","B","C"}});//推荐写法

五、使用反射操作字段

Field类提供两组方法操作字段:

1)xxx getXxx(Object obj):获取obj对象该Field的字段值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,Object get(Object obj);

2)void setXxx(Objectobj,xxx val):将obj对象的该Field字段设置成val值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,void set(Object obj, Objectvalue);

步骤:

     1.获取类

     2.获取字段

     3.赋值(set(c.newInstance(),””));{如果为私有的话设置可接受}

如:

      Class clz = Cat.class;//获取类
      Field[] f = clz.getDeclaredFields();//获取字段
      Field fi = clz.getDeclaredField("name");
      System.out.println(fi.getName());//name

      Cat c = clz.newInstance();
      fi.setAccessible(true);
      fi.set(c, "zhangsan");//将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
      Object o = fi.get(c);
      System.out.println(o);//取出成功

      fi = clz.getDeclaredField("age");
      fi.setAccessible(true);
      fi.set(c, 21);
      int i = fi.getInt(c);//左边的接受类型已经写成了int,右边的返回类型就也必须是int
      System.out.println(i);//获取成功

六、利用反射获取泛型的类型

步骤:

     1.获取当前类

     2.获取目标字段

     3.获取包含泛型类型的类型 getGenericType()

     4.强转至子类ParameterizedType,因为Type没有任何对应的方法,TypegetRawType()//返回被泛型限制的类型

     5.获得泛型真正的类型Type[] getActualTypeArguments()

如:

     Map map = new HashMap();
     Class c = GetGenericTypeDemo14.class;
     Field f = c.getDeclaredField("map");
     Class cl = f.getType(); //返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型.//其结果为:interface java.util.Map
     Type t = f.getGenericType();// 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。 Type是Class的接口; 包含泛型的类型//其结果为:java.util.Map
     //因为Type这个类里面没有任何的方法,所以需要调用子类的方法,那么大的类型转到小的类型,需要强转
     ParameterizedType pt = (ParameterizedType)t;//强转到其子类
     t = pt.getRawType();//返回 Type 对象,表示声明此类型的类或接口。
     Type t1=pt.getOwnerType(); //返回 Type 对象,表示此类型是其成员之一的类型。
     Type[] ts = pt.getActualTypeArguments();//返回表示此类型实际类型参数的 Type对象的数组。
     for (Type type : ts) {

            System.out.println(type);//其结果为:class java.lang.String,class java.lang.Integer

七、反射的作用

作用:实现框架功能。

例如:我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

框架要解决的核心问题:我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?

因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。

框架与工具类的区别:工具类被用户的类调用。而框架则是调用用户提供的类。

如:你做的门调用锁,锁是工具;你做的门被房子调用,房子是框架;锁和房子都是别人提供的。

八、JDK1.5新特性

枚举:在视频中没有详细讲解,可查看http://blog.csdn.net/kangmiao89757/article/details/10813171

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

枚举的应用

      1、通过enum关键字定义枚举类,枚举类是一个特殊的类,每个元素都是该类的一个实例对象。

      2、用枚举类规定值。以后用此类型定义的值只能是这个类中规定好的那些值,若不是这些值,编译器不会通过。

      3、好处:在编译时期就会发现错误,表明值不符合,减少了运行时期的错误。

      4、如果调用者想打印枚举类中元素的信息,需由编写此类的人定义toString方法。

注:枚举类是一个class,而且是一个不可被继承的final类,其中的元素都是类静态常量。

常用方法

      A)构造器:

             1.构造器只是在构造枚举值的时候被调用。

             2.构造器只有私有private,绝不允许有public构造器。这样可以保证外部代码无法重新构造枚举类的实例。因为枚举值是public static final的常量,但是枚举类的方法和数据域是可以被外部访问的。

             3.构造器可以有多个,调用哪个即初始化相应的值。

      B)非静态方法:(所有的枚举类都继承了Enum方法)

             1.String toString() ;//返回枚举量的名称

             2.int ordinal() ;//返回枚举值在枚举类中的顺序,按定义的顺序排

             3.Class getClass() ;//获取对应的类名

             4.String name();//返回此枚举常量的名称,在其枚举声明中对其进行声明

      C)静态方法:

             1.valueOf(String e) ;//转为对应的枚举对象,即将字符串转为对象

             2.values() ;//获取所有的枚举对象元素

示例:

package cn.itheima;

public class EnumDemo {
	public static void main(String[] args) {
		WeekDay weekDay=WeekDay.MON;
		System.out.println(weekDay);//输出枚举常量名
		System.out.println(weekDay.name());//输出对象名
		System.out.println(weekDay.getClass());//输出对应类
		System.out.println(weekDay.toString());//输出枚举对象名
		System.out.println(weekDay.ordinal());//输出此对象在枚举常量的次序
		System.out.println(WeekDay.valueOf("WED"));//将字符串转化为枚举常量
		System.out.println(WeekDay.values().length);//获取所以的枚举元素,并打印其长度
	}
	//定义枚举内部类
	public enum WeekDay{
		SUN(1),MON,TUE,WED,THI,FRI,SAT;//分号可有可无,但如果下面还有方法或其他成员时,分号不能省。
		//而且当有其他方法时,必须在这些枚举变量的下方。

		//无参构造器
		private WeekDay(){
			System.out.println("First");
		}
		//带参数的构造器
		private WeekDay(int day){
			System.out.println("Second");
		}
	}
}

枚举的高级应用

       1、枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。

       2、枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。

       3、带构造方法的枚举,构造方法必须定义成私有的。枚举元素MONMON()的效果一样,都是调用默认的构造方法。

       4、带方法的枚举。

示例:

/*
 * 抽象的枚举方法
 * 此时枚举中的常量需要子类来实现,这是可以利用内部类的方式来定义枚举常量
 * 带方法的枚举
	1)定义枚举TrafficLamp
	2)实现普通的next方法
	3)实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类
	4)用类似内部类的方式进行定义。
	5)增加上表示时间的构造方法
 * */

package cn.itheima;

public class EnumTest {
	public enum TrafficLamp{
		RED(30){
			public TrafficLamp nextLamp(){
				return GREEN;
			}
		},
		GREEN(30){
			public TrafficLamp nextLamp(){
				return YELLOW;
			}
		},
		YELLOW(5){
			public TrafficLamp nextLamp(){
				return RED;
			}
		};
		private int time;
		//构造器
		private TrafficLamp(int time){
			this.time=time;}
		//抽象方法
		public abstract TrafficLamp nextLamp();
	}		
}

注:1.所有的枚举都继承自java.lang.Enum类。由于Java不支持多继承,所以枚举对象不能再继承其他类。

        2.switch语句支持int,char,enum类型,使用枚举,能让代码可读性更强。

小结:

        1.匿名内部类比较常用

        2.类的方法返回的类型可以是本类的类型

        3.类中可定义静态常量,常量的结果就是自己这个类型的实例对象

        4.枚举只有一个成员时,可以作为一种单例的实现方式。

 

还有其他新特性。如:静态导入、高级for循环、可变参数、基本数据类型的自动拆箱与装箱等前面文档都有讲过,在这里就不再讲解了。

九、正则表达式

视频中没有讲解,可查看http://blog.csdn.net/kangmiao89757/article/details/10807567

正则表达式:

概念:符合一定规则的表达式。

作用:用于专门操作字符串。

特点:用于一些特定的符号来表示一些代码操作,这样可以简化书写。所以学习正则表达式,就是在学习一些特殊符号的使用。

好处:可以简化对字符串的复杂操作。

弊端:符合定义越多,正则越长,阅读性越差。

常见符号:

1、字符

        x                  字符 x

        \\                 反斜线字符

        \t                 制表符 ('\u0009')

        \n                 新行(换行)符 ('\u000A')

        \r                 回车符 ('\u000D')

        \f                 换页符 ('\u000C')

        \a                 报警 (bell) ('\u0007')

2、字符类

        [abc]                      ab c(简单类)

        [^abc]                   任何字符,除了 ab  c(否定)

        [a-zA-Z]                a z A Z,两头的字母包括在内(范围)

        [a-d[m-p]]             a d m p[a-dm-p](并集)

        [a-z&&[def]]        de f(交集)

        [a-z&&[^bc]]        a z,除了 b  c[ad-z](减去)

        [a-z&&[^m-p]]     a z,而非 m  p[a-lq-z](减去)

3、预定义字符类

        .                         任何字符(与行结束符可能匹配也可能不匹配)

        \d                        数字:[0-9]

        \D                       非数字: [^0-9]

        \s                        空白字符:[ \t\n\x0B\f\r]

        \S                       非空白字符:[^\s] 

        \w                       单词字符:[a-zA-Z_0-9]

        \W                      非单词字符:[^\w]

4、边界匹配器

        ^                         行的开头

        $                         行的结尾

        \b                        单词边界

        \B                       非单词边界

        \A                       输入的开头

        \G                       上一个匹配的结尾

        \Z                       输入的结尾,仅用于最后的结束符(如果有的话)

        \z                        输入的结尾

5Greedy数量词(X表示字符X或者匹配的规则)。

        X?                       X,一次或一次也没有

        X*                       X,零次或多次

        X+                       X,一次或多次

        X{n}                    X,恰好 n

        X{n,}                   X,至少 n

        X{n,m}                X,至少 n次,但是不超过 m

6、组和捕获

       捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:

        1     ((A)(B(C)))

        2     \A

        3     (B(C))

        4     (C)

       组零始终代表整个表达式。在替换中常用$匹配组的内容。

正则表达式具体功能:

1)匹配:String类中的booleanmatches(String regex)方法。用规则匹配整个字符串,只要有一处不符合规则,就匹配结束,返回false

2)切割:String类中的String[]split(String regex)方法。 按叠词完成切割:为了让规则被重用,可将规则封装成一个组,用()完成。组的出现都有编号,从1开始。想要使用已有的组可通过\nn就是组的编号)的形式来获取。对于组中所匹配的字符,可以用$n来获取。$在正则中表示行的结尾,所以出现在正则中不能用来表示组,一般用于替换中。如下面功能中。

3)替换: String replaceAll(String regex,String replacement)方法。

4)获取:将字符串中的符合规则的子串取出。

      操作步骤:1.将正则表达式封装成对象。2.让正则对象和要操作的字符串相关联。3.关联后,获取正则匹配引擎。4.通过引擎对符合规则的子串进行操作,比如取出。

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

你可能感兴趣的:(Java学习笔记)