黑马程序员-张孝祥Java基础加强(PART1)

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


知识点:

1.Java5的几个新特性,包括静态导入、可变参数、增强for循环、基本数据类型的自动装箱与拆箱、枚举;

2.反射;

3.内省;

4.注解。


一、Java5的新特性

1.静态导入

import语句可以导入一个类或一个保重的所有类,而import static语句可以只导入一个类中的所有或某个静态方法,这就是静态导入。静态导入某个类或静态方法之后,可以直接调用导入的静态方法无需通过类名。例如:

import static java.lang.Math.*;

public class StaticImport {

	public static void main(String[] args) {
		System.out.println(max(3, 6));
		System.out.println(abs(3-7));
	}

}

在上面的代码中通过 import static java.lang.Math.*;语句导入了Math类的所有静态方法。也可以只导入某一个静态方法,如 import static java.lang.Math.max;


2.可变参数

如果一个方法的参数个数不确定,可以使用可变参数,格式:参数类型... 变量名

特点:

(1)只能出现在参数列表的最后,如void method(int x, int... args);不可以写成void method(int... args, int x);

(2)...位于变量类型和变量名之间,前后有没有空格都可以;

(3)调用含有可变参数的方法时,编译器会为该可变参数创建一个隐含的数组,在方法体中可以通过数组的方式操作可变参数。

示例:

public class VirableParameter {

	public static void main(String[] args) {
		System.out.println(getSum(3, 3));
		System.out.println(getSum(3, 5, 9));
	}
	
	public static int getSum(int x, int... args) {
		int sum = x;
		for (int i=0;i
   
   

3.增强for循环
增强for循环的语法为for(type 变量名:集合变量名){...}。
示例:在可变参数的示例代码中,对可变参数的操作可以使用增强for循环
for (int arg:args) 
{
	sum += arg;
}
注意:
(1)迭代变量必须在()中定义;
(2)集合变量可以是数组或实现了 Iterable接口的集合类

4.基本数据类型的自动装箱与拆箱
所谓的自动装箱和拆箱,就是基本数据类型可以直接和对应的表示该数据类型的类进行赋值和运算操作,无需调用方法。
Integerint举例, Integer num = 12;就是自动装箱, System.out.println(num+12);就是自动拆箱。
示例:
public class AutoBox {

	public static void main(String[] args) {
		Integer iObj = 12;
		System.out.println(iObj + 32);

		Integer i1 = 14;
		Integer i2 = 14;
// 		Integer i1 = 142;
// 		Integer i2 = 142;
		System.out.println(i1==i2);
		
		Integer i3 = Integer.valueOf(13);
		Integer i4 = Integer.valueOf(13);
		System.out.println(i3==i4);
	}

}

在上面的代码中,当i1和i2的赋值是处于-128~127的范围之内的同一个数时,两者是同一个对象,否则两者是不同的两个对象。i3和i4的情况相同。这称为享元模式。
享元模式( Flyweight):如果有很多很小的对象,他们有很多相同的东西,那么就可以把他们相同的东西包装成一个对象,把不同的东西变成外部的属性,作为方法的参数传入对象。这样做的好处就是可以减少资源的浪费。

5.枚举
枚举就是让某个类型变量的取值只能是若干固定值中的一个。枚举就是一种特殊的类,其中的每个元素就是该类的一个实例对象。
(1)通过创建一个代表一周的普通类来实现枚举的作用
public abstract class Weekday1 {
    //将构造函数私有化,这样就不能在外部创建对象
	private Weekday1(){}
	
	//在类的内部创建七个静态final子类来分别表示七天
	public static final Weekday1 SUN = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			return MON;
		}
	};
	public static final Weekday1 MON = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			// TODO Auto-generated method stub
			return TUE;
		}
	};
	public static final Weekday1 TUE = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			return WED;
		}
	};
	public static final Weekday1 WED = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			return THU;
		}
	};
	public static final Weekday1 THU = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			return FRI;
		}
	};
	public static final Weekday1 FRI = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			return SAT;
		}
	};
	public static final Weekday1 SAT = new Weekday1() {
		@Override
		public Weekday1 nextDay() {
			return SUN;
		}
	};
	
    /*
     *获取下一天的方法,通过将其抽象,把方法的实现转移到子类中去,
     *从而避免了写大量if else判断语句的麻烦
     */
	public abstract Weekday1 nextDay();
	
	/*public Weekday nextDay() {
		if(this==SUN)
			return MON;
		else
			return SUN;
	}*/
	
	public String toString() {
		if(this==SUN)
			return "SUN";
		else if(this==MON)
			return "MON";
		else if(this==TUE)
			return "TUE";
		else if(this==WED)
			return "WED";
		else if(this==THU)
			return "THU";
		else if(this==FRI)
			return "FRI";
		else if(this==SAT)
			return "SAT";
		else 
			return null;
	}
}

(2)枚举类的基本应用
public class EnumTest {

	public static void main(String[] args) {
		
		//当调用枚举中的某个元素时,会先实例化枚举中的所有元素
		Weekday wd2 = Weekday.THU;
		System.out.println(wd2);//打印元素的名字
		System.out.println(wd2.toString());//同上
		System.out.println(wd2.name());//同上
		System.out.println(wd2.ordinal());//元素在美剧中的序号
		System.out.println(Weekday.valueOf("MON"));//将字符串形式转换成枚举对象
		System.out.println(Weekday.values().length);//获得枚举中的所有元素,返回一个数组
		
	}
	
	public enum Weekday {
		SUN,MON(2),TUE,WED,THU,FRI,SAT;//这些元素必须放在最前面
		private Weekday(){//构造方法只能是私有的
			System.out.println("first");
		}
		private Weekday(int day) {//有参的构造函数
			System.out.println("second");
		}
	}
}

(3)枚举的高级应用
枚举相当于一个类,可以在其中定义构造方法、成员变量、普通方法和抽象方法。但所有这些成员都必须放在所有枚举元素列表之后,通过分号相分离。
下面是一个表示交通灯的枚举类。
public enum TrafficLamp {
		RED(30){//这是内部类,编译后.class文件名字为TrafficLamp$1.class
			@Override
			public TrafficLamp nextLamp() {
				return GREEN;
			}
		},
		GREEN(45){//调用父类的有参构造函数
			@Override
			public TrafficLamp nextLamp() {
				return YELLOW;
			}
		},
		YELLOW(5){
			@Override
			public TrafficLamp nextLamp() {
				return RED;
			}
		};
		
		private int time;//成员变量
		private TrafficLamp(int time) {//有参的构造方法
			this.time = time;
		}
		
		//抽象的方法
		public abstract TrafficLamp nextLamp();
		//普通的方法
		/*public TrafficLamp nextLamp() {
			switch(this) {
			case RED:
				return GREEN;
			case GREEN:
				return YELLOW;
			case YELLOW:
				return RED;
			}
			return null;
		}*/
	}

该枚举中的每个元素都是该枚举类的一个子类,并且是内部类,调用了父类(即该枚举)的有参构造,复写并实现了父类的抽象方法。调用方式, TrafficLamp.RED.nextLamp();
当枚举只有一个成员时,可以作为单例的一种实现方式。

二、反射
1.反射的基石——Class类

Class类是描述Java类这类事物的类,就好像Person类是描述人这类事物的类。Class类描述了类的名字、类的访问属性、类所属的包名、字段名称的列表、方法名称的列表等等。

一个类被类加载器加载到内存中,占用一片存储空间,这片空间里的内容就是类的字节码,不同类的字节码是不同的,这些字节码可以分别用一个对象来表示,这些对象显然具有相同的类型,这个类型就是Class类。也即是说,Class类是内存中字节码的抽象,Class类的实例对象就是对应类在内存中的字节码。一个类在虚拟机中通常只有一份字节码。

得到字节码对应的实例对象的(Class类型)的三种方法:

(1)类名.class,比如String.class

(2)对象.getClass(),比如new Date().getClass()

(3)class.forName(“完整类名”),比如class.forName("java.util.Date")

有九种预定义的Class对象,表示八种基本数据类型和void。这些类对象由Java虚拟机创建,与其表示的基本类型同名,即booleanbytecharshortintlongfloatdouble。需要注意的是int.class不等于Integer.class,因为Interger是引用数据类型,但是int.classInteger.TYPE相同,因为Integer.TYPE就是表示int的Class实例。其他类同。

String s = "abc";
		
//三种获得Class实例对象的方法
Class cls1 = s.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
//下面打印结果均为true,说明这三个是同一份字节码
System.out.println(cls1==cls2);//true
System.out.println(cls1==cls3);//true

//判断是否是原始数据类型的方法
System.out.println(cls1.isPrimitive());//false
System.out.println(int.class.isPrimitive());//true
System.out.println(int.class == Integer.class);//false
System.out.println(int.class == Integer.TYPE);//true
System.out.println(int[].class.isPrimitive());//false
//判断是否是数组的方法
System.out.println(int[].class.isArray());//true

注意:加载字节码并调用其方法,没有看到类的静态代码块被执行,只有当第一个实例对象被创建时,静态代码块才会执行。准确的说,静态代码块不是在类被加载的时候被执行的,而是在第一个实例对象被创建时才执行的。


2.反射

反射就是把Java类中的各种成分映射成相应的Java类。也就是说,Java类用Class类来表示,Java类中的组成部分,如成员变量、方法、构造方法、包等也用相应的类来表示,它们是Filed、Method、Constructor、Package等。一个类的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。

(1)Constructor类

Constructor代表某个类中的一个构造方法。

//获得某个类所有的构造方法
Constructor[] constructors= Class.forName("java.lang.String").getConstructors();

//获得某个类的某一个构造方法,将构造方法参数的Class对象穿进去
//通过参数个数和类型进行判断
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

//创建实例对象
String str = new String(new StringBuffer("abc"));//通常方式
//得到方法的时候需要传递参数类型,调用方法的时候需要传递同样类型的变量
String str = (String)constructor.newInstance(new StringBuffer("abc"));//反射方式

//Class.newInstance()方法
//方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
String obj = (String)Class.forName("java.lang.String").newInstance();

(2)Field类

Filed类代表某个类中的一个成员变量。

定义一个ReflectPoint类进行演示。

public class ReflectPoint {

	private int x;
	public int y;
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
}
下面是Filed类的示例代码

//创建一个ReflectPoint类
ReflectPoint pt1 = new ReflectPoint(5, 7);

//通过getFiled方法,将属性名传进去,获得属性定义
Field fieldY = pt1.getClass().getField("y");
//通过Filed的类的get方法,将操作对象传进去,获得属性值
System.out.println(fieldY.get(pt1));
//因为x是私有属性,getFiled无法获得其定义,getDeclaredField可以
Field fieldX = pt1.getClass().getDeclaredField("x");
//要想获得属性x的值,必须通过下面语句,这称为暴力反射
//fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”。

	private static void changeStringValue(Object obj) throws Exception {
		//获得obj对应的Class对象
		Class cls = obj.getClass();
		//获得所有字段定义
		Field[] fds = cls.getFields();
		for (Field fd:fds) {
		    //循环判断每个字段是否是String类型
			if(fd.getType()==String.class){
			    //如果是的话,获取属性的值
				String oldValue = (String)fd.get(obj);
				//将所有'b'换成'a'
				String newValue = oldValue.replace('b', 'a');
				//把改变后的值赋给该字段
				fd.set(obj, newValue);
			}
		}
	}
(3)Method类

Method类代表某个类中的一个成员方法

//得到类中的某一个方法,传递的参数为方法名字符串和该方法对应参数的Class对象
Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);

//调用方法
System.out.println(str.charAt(2));//通常方法
System.out.println(charAt.invoke(str,2));//反射方法

/*
jdk1.4和jdk1.5的invoke方法的区别:
jdk1.5:public Object invoke(Object obj,Object... args)
jdk1.4:public Object invoke(Object obj,Object[] args)
按1.4的语法,需要讲一个数组作为参数传递给invoke方法,数组中的每个元素分别对应被
调用方法中的一个参数,所以调用charAt方法的代码也可以写成下面这种形式
但是不能传递基本数据类型的数组,因为会被当做一个参数元素
*/
charAt.invoke(str,new Object[]{2});
如果传递给Method对象的第一个参数为null,说明该Method对象对应的是一个静态方法。

(4)用反射方法执行某个类中的main方法

目标:通过用户提供的类名,执行该类的main方法。

先定义一个包含主函数的类

{CSDN:CODE:209806
调用主函数的代码

//由用户传递类名,更加灵活
String startingClassName = args[0];
//获得主函数的定义
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
/*
 * 问题:
 * main方法只接受一个String类型的数组,在这里如果直接传递一个String数组作为参数,在jdk1.4中,
 * invoke方法第二个参数接收的是一个数组,会自动将数组拆开作为不同的参数,这样的话前面的String
 * 数组就会拆分成几个String类型的参数,这样就不符合main的方法定义了。
 * 以下两种解决办法都可以
 */
//将String数组再包装进一个Object数组,这样将Object数组拆分后得到的就是String数组
mainMethod.invoke(null, new Object[]{new String[]{"111", "222", "444"}});
//将String数组转成Object类型的对象,编译器不把其当做数组看,就不会拆分了
mainMethod.invoke(null, (Object)new String[]{"111", "222", "444"});
注意: Object和String具有父子关系,但 Objec[]String[]没有父子关系,所在上面的代码中不能写成
mainMethod.invoke(null, new Object[]{new Object[]{"111","222","444"}});
mainMethod.invoke(null, (Object)new Object[]{"111","222","444"});
(5)数组的反射

import java.lang.reflect.Array;
import java.util.Arrays;

public class ReflectTest{
	public static void main(String[] args) throws Exception {
		
		//具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
		int[] a1 = new int[]{1,2,3};
		int[] a2 = new int[4];
		int[][] a3 = new int[3][4];
		String[] a4 = new String[]{"a","b","c"};
		System.out.println(a1.getClass()==a2.getClass());//true
//		System.out.println(a1.getClass()==a3.getClass());//编译器不通过
//		System.out.println(a1.getClass()==a4.getClass());//编译器不通过
		System.out.println(a1.getClass().getName());//[I,代表整形数组
		System.out.println(a3.getClass().getName());//[[I,代表二维整型数组
		System.out.println(a4.getClass().getName());//[Ljava.lang.String;代表字符串数组

		//代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
		System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object
		System.out.println(a3.getClass().getSuperclass().getName());//java.lang.Object
		System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object
		
		/*
		 *基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型,
		 *非基本类型的一维数组,既可以被当做Object类型,也可以当做Object[]类型。
		 */
		Object aObj1 = a1;
		Object aObj2 = a3;
		Object aObj3 = a4;
//		Object[] aObj4 = a1;//编译器报错
		Object[] aObj5 = a3;
		Object[] aObj6 = a4;
		
		//下面打印的是地址值
		System.out.println(a1);
		System.out.println(a4);
		/*
		 *通过工具类Arrays的asList方法,将数组转成List
		 *不同之处在于:
		 *int[]数组会整体作为List的一个元素
		 *String[]数组中的每个String会分别作为List的一个元素
		 */
		System.out.println(Arrays.asList(a1));
		System.out.println(Arrays.asList(a4));
		
		//打印数组的方法,使用反射方式
		printObject(a1);
		printObject(a4);
		printObject("nihao");
	}

    //Array类是完成数组反射操作的类
	private static void printObject(Object obj) {
		//获得obj的Class实例
		Class clazz = obj.getClass();
		//判断是否是数组
		if(clazz.isArray()){//如果是
		    //获得数组长度
			int len = Array.getLength(obj);
			System.out.println(len);
			//遍历数组
			for(int i=0;i
    
    
(6)反射的作用——实现框架功能
什么是框架?
框架就好比开发商做的毛坯房,开放商将房子卖给用户,需要住户自己去装修,比如安装门窗。房子就是框架,用户将门窗插入框架中。
框架与工具类的区别:框架调用用户提供的类,工具类用户的类调用。
框架要解决的核心问题是什么?
我在写框架(盖房子)时,用户可能还在上小学,还不会写程序,那框架怎么调用用户后来写的类呢?因为在无法知道要被调用的类名,所以程序中无法直接new某个类的实例对象,需要通过反射方式来做。
示例代码:
package com.itheima.day1;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;

public class ReflectTest2 {
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		//资源文件要用完整的路径,但完整的路径不是硬编码,而是运算出来的
		//加载资源文件的几种方式
		/*
		//需要将资源文件放在工程的根目录下,不合适
		InputStream is = new FileInputStream("config.properties");
		//通过类加载器加载资源文件,需要提供资源文件的完整路径,路径最前面不加/
		InputStream is = ReflectTest2.class.getClassLoader().getResourceAsStream("com/itheima/day1/resourses/config.properties");
		//直接通过Class对象加载资源文件,资源文件放在和.class文件相同的目录下
		InputStream is = ReflectTest2.class.getResourceAsStream("config.properties");
		//把资源文件放在单独的资源目录下更合适,提供相对路径即可
		InputStream is = ReflectTest2.class.getResourceAsStream("resourses/config.properties");
		*/
		//也可通过绝对路径访问
		InputStream is = ReflectTest2.class.getResourceAsStream("/com/itheima/day1/resourses/config.properties");
		/*
		 * 在上面的路径中,最前面加斜杠表示绝对路径,斜杠代表根目录,不加斜杠表示相对路径,
		 * 即相对于ReflectTest2.class所在目录的路径
		 * 不管是相对还是绝对,其内部还是通过类加载器进行加载
		 * struts等框架的配置文件都是放在classpath路径下的,
		 * 其内部是用类加载器来加载配置文件的
		 */
		 
		//将资源文件的数据读取到Properties对象中
		Properties props = new Properties();
		props.load(is);
		is.close();
		//获取要加载的类名
		String className = props.getProperty("className");
		
		//根据资源文件来确定要加载哪个类
		Collection collection = (Collection)Class.forName(className).newInstance();
//		Collection collection = new HashSet();//传统方式
		
		ReflectPoint rp1 = new ReflectPoint(3, 3);
		ReflectPoint rp2 = new ReflectPoint(6, 6);
		ReflectPoint rp3 = new ReflectPoint(3, 3);
		
		collection.add(rp1);
		collection.add(rp2);
		collection.add(rp3);
		collection.add(rp1);
		
		/*
		 *这里有一个HashSet的知识点需要注意
		 *当一个对象存进HashSet集合以后,就不能修改这个对象中参与计算哈希值的
		 *字段了,因为HashSet是通过对象的哈希值进行查找的,如果在存进去之后改变
		 *了哈希值,就无法找到这个对象,从而造成内存泄露。
		 */
		rp1.y = 9;
		System.out.println(collection.contains(rp1));
//		System.out.println(collection.remove(rp1));
		
		System.out.println(collection.size());
	}

}

三、内省(IntroSpector)

1.JavaBean

JavaBean是一种特殊的Java类,主要用于传递数据信息,这种Java类的方法主要用于访问私有的字段,且方法名符合某种命名的规则。

如果要在两个模块之间传递对个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,需要通过相应的方法来访问,即settergetterJavaBean的属性是根据其中的settergetter方法来确定的,而不是根据其中的成员变量。如果方法名为setIdgetId,那属性名就是id,也就是说去掉setget,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,就把首字母也改成小写,否则保持不变。如setId的属性名为id,isLast的属性名为last,setCPU的属性名为CPU。总之,一个类被当做JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到Java类内部的成员变量。

jdk中提供了对JavaBean进行操作的一套API,对JavaBean的操作就称为内省。


2.内省综合案例

首先更改ReflectPoint类,使其符合JavaBean的定义。

package com.itheima.day1;

import java.util.Date;

public class ReflectPoint {
    Date birthday = new Date();
	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	private int x;
	public 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;
	}

}

package com.itheima.day1;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class IntroInspectorTest {
/
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		ReflectPoint rp = new ReflectPoint(4,8);
		
		//将内省的操作封装进两个方法中
		String propertyName = "x";
		Object retValue = getProperty(rp, propertyName);
		System.out.println(retValue);
		
		Object value = 11;
		setProperty(rp, propertyName, value);
		System.out.println(rp.getX());

	}

	private static Object getProperty(Object obj, String propertyName)
			throws IntrospectionException, IllegalAccessException,
			InvocationTargetException {
		//BeanInfo对象中包含得到的JavaBean的所有信息	    
		BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
		//获得JavaBean的所有属性
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		for(PropertyDescriptor pd : pds) {
		    //判断是否是想要的属性名
			if(pd.getName().equals(propertyName)){
			    //如果是就获取它的读取方法
				Method methodGet = pd.getReadMethod();
				//将对象传入得到方法的返回值
				retValue = methodGet.invoke(obj);
			}
		}
		
		return retValue;
	}

	private static void setProperty(Object obj, String propertyName,
			Object value) throws IntrospectionException,
			IllegalAccessException, InvocationTargetException {
        //创建对象的属性定义
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj.getClass());
		//获取属性的写方法
		Method methodSet = pd.getWriteMethod();
		//将对象和要设置的值传入
		methodSet.invoke(obj, value);
	}

}

3.BeanUtils工具包

BeanUtils是Apache提供的一个操作JavaBean的工具包。

在上面代码的基础上,BeanUtils操作方式如下:

BeanUtils.setProperty(rp, "x", 33);
		System.out.println(BeanUtils.getProperty(rp, "x").getClass().getName());
		
		/*1.7新特性
		 * Map map = {name:"zxx", age:15};
		BeanUtils.setProperty(map, "name", "lhm");*/
		//BeanUtils可以操作属性的属性
		BeanUtils.setProperty(rp, "birthday.time", "123");
		System.out.println(BeanUtils.getProperty(rp, "birthday.time"));
		
		//PropertyUtils也是一个操作JavaBean的工具类
		PropertyUtils.setProperty(rp, "y", 333);
		System.out.println(PropertyUtils.getProperty(rp, "y").getClass().getName());
注意:使用BeanUtilsget属性时返回的结果为字符串,set属性时可以接收任意类型的对象,通常使用字符串;使用PropertyUtilsget属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型。


四、注解

1.了解注解以及几个基本注解

注解相当于一种标记,在程序中加了注解就相当于为程序打上了某种标记,以后编译器、开发工具和其他程序可以通过反射来了解你的类和各种元素上有无何种标记。标记可以加在包、类、字段、方法、方法的参数和局部变量上。

三种基本的注解:

@SuppressWarnings,压缩警告。比如调用一个过时的方法,编译器会发出警告,加上压缩警告的注解就不会了;

@Deprecated,表明该元素已过时。当别人再调用该方法是,编译器会给出警告。

@Override,表示覆盖。该注解可以防止,继承自父类的方法定义写错。

通过Class类的isAnnotationPresentgetAnnnotation方法可以判断和获取某个类中的注解。


2.自定义注解及其应用

package com.itheima.day2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


//元注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE
public @interface ItcastAnnotation {

}
@Retention@Target也是注解,称为元注解,即注解的注解。

@Retention表示注解存在的阶段,取值有三种,RetetionPolicy.SOURCERetetionPolicy.CLASSRetetionPolicy.RUNTIME,分别对应Java原文件、.class文件和内存。

@Target表示注解可以修饰的元素类型,取值有ElementType.TYPEElementType.FIELDElementType.METHODElementType.PARAMETERElementType.CONSTRUCTORElementType.LOCAL_VIRIABLEElementType.ANNOTATION_TYPEElementType.PACKAGE,,分别对应类(包括普通类,接口,注解、枚举)、字段、方法、参数、构造方法、局部变量、注解和包。


3.为注解增加属性

枚举的属性可以是基本数据类型、StringClass、枚举、其他注解,以及这些类型的数组。

package com.itheima.day2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.itheima.day1.EnumTest;


//元注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface ItcastAnnotation {
	String color() default "blue";//字符串
	String value();
	int[] arrayAttr() default {1,2,3};//数组
	EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.GREEN;//枚举
	MetaAnnotation annotationAttr() default @MetaAnnotation("nimei");//其他注解
}
如果注解中有一个名称为value的属性,且你只想设置value属性(其他属性都采用默认值或者只有一个value属性),那么可以省略”value=“部分。

如果数组属性的值只有一个元素,这时属性值可以省略大括号。


---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值