1、枚举类
Jdk1.5扩展了switch,它除了可以接收int、byte、char、short外,还可以接收一个枚举类型。以前我们想控制switch的输入时的做法如下
package cn.itcast.domain;
public class Student {
private String name;
private String grade;
public String getName() {
return name;
}
public void setName(Stringname) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
//有时候我们为了控制输入可以向下面这样做
//当输入的不是A/B/C/D/E时就返回错误,但是这种方式没有从源头控制错误
//因为需要输入了才能判断是否正确。
if(!grade.matches("[ABCDE]")){
throw new RuntimeException("对不起,您的输入有误。");
}
this.grade = grade;
}
}
但是这种方式没有从源头控制住错误,改进方式可以将grade申明成一个类
package cn.itcast.domain;
public class Grade {
private Grade(){}
public static GradeA = new Grade();
public static GradeB = new Grade();
public static GradeC = new Grade();
public static GradeD = new Grade();
public static GradeE = new Grade();
}
此时,如果我们再想要输入不是A/B/C/D/E中的值就会报错,如s.setGrade(Grade.F);因为grade只能取规定的五个值。于是也不需要对输入进行判断了。而这里注意:如果grade只能取一个值,就变成单例设计模式了。因为grade中申明的值是静态的,也就表明整个程序中只有一个grade对象,即单例。同时这个类的构造函数是私有的,我们不能new。但是这种方式有点笨拙,这里我们使用枚举进行改进。
package cn.itcast.domain;
public enum Grade{
A,B,C,D,E;
}
注意:这里我们在使用枚举类的时候需要将其申明为public,不然后面我们不能使用其里面的值。当然我们可以将此枚举类申明为内部类。枚举其实就相当于一个类,然后我们申明了五个类对象,我们可以和之前的做法对照看。当然我们若想包含更多的信息,可以继续改进
package cn.itcast.domain;
public enum Grade{
A("100-90"),B("89-80"),C("79-70"),D("69-60"),E("59-0");
private String value;
private Grade(String value){
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
}
说明:因为A/B/C/D/E就相当于五个类对象,当然可以拥有相关的属性,而这里我们不需要setter方法。但是我们有时候想让每个类对应一个其他叫法的等级名字,比如A对应优秀,A本身就是一个类,当然也可以拥有相关的方法
package cn.itcast.domain;
public enum Grade{
A("100-90"){
public String toLocaleString(){
return"优";
}
},B("89-80"){
public String toLocaleString(){
return"良";
}
},C("79-70"){
public String toLocaleString(){
return"中";
}
},D("69-60"){
public String toLocaleString(){
return"及格";
}
},E("59-0"){
publicString toLocaleString(){
return"差";
}
};
public abstract String toLocaleString();
private String value;
private Grade(String value){
this.value = value;
}
public String getValue() {
return value;
}
}
说明:但是这里注意,我们不能直接申明一个一般的方法,因为那样的话就被所有类共有了,这里我们申明一个抽象方法,但是又不能new一个类,所以只能使用匿名内部类来实现抽象方法了。我们在使用的时候如果想得到对应的分数或者中文等级:
System.out.println(s.getGrade().getValue());
System.out.println(s.getGrade().toLocaleString());
2.反射(工程reflect)
这里我们先给出一个实体类
src下
package cn.itcast.domain;
public class Person {
public String name; //public字段
private int age; // private字段
public final String password = "123"; //final字段
public String getName() {//public无参有返回值
return name;
}
public void setName(String name) {//public有参无返回值
this.name = name;
}
public int getAge() {//public无参有返回值
return age;
}
public void setAge(int age) {//public有参无返回值
this.age = age;
}
public Person(){//无参public构造方法
//System.out.println("person!!!!!");
}
public Person(String name){//有参public构造方法
System.out.println(name);
}
private Person(int name){//有参private构造方法
System.out.println(name);
}
public void eat(){//public无参无返回值
System.out.println("eat!!!!");
}
public void run(String address){ //public有参无返回值
System.out.println("跑到" + address);
}
public void run(String address,int num[],String ss[]){
System.out.println("跑到" + address + "," + num);
}//public多参无返回值
public String test(String str){ //public有参有返回值
return str + "aaaa";
}
private String test2(String str){ //private无参有返回值
return str + "aaaa";
}
public static String test3(String str){
return str + "aaaa";//public static有参有返回值
}
public static void main(String[] args) {
System.out.println(args[0]); //public static有参无返回值
}
}
下面我们反射这个类中的各个方法:
src下
package cn.itcast.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
import cn.itcast.domain.Person;
public class Demo1 {
//反射构造方法
//反射类的无参构造方法public Person()
@Test
public void test1() throws Exception{
//取得某个类的字节码,一般我们都是用这种方式取得,其他的方式如类名.class和
//对象.getClass()一般都不用
Class c1 = Class.forName("cn.itcast.domain.Person");
//null表示无参
Constructor<Person> c = c1.getConstructor(null);
//需要注明无参
Object obj = c.newInstance(null);
System.out.println(obj.getClass().getName());
}
//反射有参构造方法public Person(String name)
@Test
public void test2() throws Exception{
Class c2 = Class.forName("cn.itcast.domain.Person");
//给出String类型的字节码
Constructor<Person> c = c2.getConstructor(String.class);
//给出相关参数的值
Person p = c.newInstance("Tom");
System.out.println(p);
}
//反射类私有、有参构造方法private Person(int name)
@Test
public void test3() throws Exception{
Class c = Class.forName("cn.itcast.domain.Person");
//取得类的私有构造函数
Constructor<Person> c1 = c.getDeclaredConstructor(int.class);
//改变私有构造函数的访问权限,让其可被访问
c1.setAccessible(true);
Person p = c1.newInstance(23);
System.out.println(p);
}
//反射方法
//反射无参、无返回值方法public void eat()
@Test
public void test4() throws Exception{
Person p = new Person();
Class c = Class.forName("cn.itcast.domain.Person");
//得到相关方法,需要给出方法名和参数名,若没有参数则为null
Method m = c.getMethod("eat",null);
//在调用的时候需要指明类对象和参数值,无参则为null
m.invoke(p, null);
}
//反射有参、无返回值方法public void run(String address)
@Test
public void test5() throws Exception{
Person p = new Person();
Class c = Class.forName("cn.itcast.domain.Person");
Method m = c.getMethod("run", String.class);
m.invoke(p, "Xian");
}
//反射多参、无返回值方法public void run(String address,int num[],String ss[])
@Test
public void test6() throws Exception{
Class c = Class.forName("cn.itcast.domain.Person");
Person p = new Person();
//这里注意数组的传入方法
Method m = c.getMethod("run", String.class, int[].class, String[].class);
m.invoke(p, "Xian", new int[]{1,2}, new String[]{"1", "2"});
}
//反射有参有返回值的公有方法public String test(String str)
@Test
public void test7() throws Exception{
Class c = Class.forName("cn.itcast.domain.Person");
Person p = new Person();
Method m = c.getMethod("test", String.class);
String result = (String) m.invoke(p, "Xian");
System.out.println(result);
}
//反射有参有返回值的私有方法private String test2(String str)
@Test
public void test8() throws Exception{
Class c = Class.forName("cn.itcast.domain.Person");
Person p = new Person();
Method m = c.getMethod("test", String.class);
//暴力反射
m.setAccessible(true);
String result = (String) m.invoke(p, "Xian");
System.out.println(result);
}
//反射静态方法public static String test3(String str)
@Test
public void test9() throws Exception{
Class c = Class.forName("cn.itcast.domain.Person");
Method m = c.getMethod("test3", String.class);
//反射静态方法不需要指定实体对象
String result = (String) m.invoke(null, "xxx");
System.out.println(result);
}
//反射main方法public static void main(String[] args)
@Test
public void test10() throws Exception{
Class c = Class.forName("cn.itcast.domain.Person");
Method m = c.getMethod("main", String[].class);
//解决办法1:
m.invoke(null, (Object)new String[]{"1", "2"});
//方法2:
m.invoke(null, new Object[]{new String[]{"xxx", "111"}});
}
/*如果这样调用m.invoke(null, new String[]{"1", "2"});会出错,这是因为如果在1.5中我们调用
* public void run(String name, String password)方法时直接使用
* method.invoke(p, "xxx", "111");即可,但是在1.4中却不能这样,必须将后面两个参数通过数组
* 传入:method.invoke(p, new Object[]{"xxx", "111"})然后会将数组解析出来分别赋给name
* 和password。但是又不能直接传入一个数组,那样的话JVM会解析出两个参数,很明显,这里我们只有一个参数。
* 于是为了兼容性在1.5中也不能直接传入两个参数。解决方法有两种:
* 方法1:将这个数组打包成一个对象,如上
* 方法2:将参数打包成一个数组,那样拆解之后还是数组,如上
* */
//反射类字段
//反射public String name
@Test
public void test11() throws Exception{
Person p = new Person();
Class c = Class.forName("cn.itcast.domain.Person");
//取得名字叫name的属性
Field f = c.getField("name");
//设置属性的值
f.set(p, "xxx");
System.out.println(p.getName());
//获取属性的值
String result = (String) f.get(p);
System.out.println(result);
}
//反射public final String password = ""
@Test
public void test12() throws Exception{
Person p = new Person();
Class c = Class.forName("cn.itcast.domain.Person");
Field f = c.getField("password");
String result = (String) f.get(p);
System.out.println(result);
}
/*注意:这个属性中定义为final,当我们new一个对象时会将其赋值。而这个值在之后的使用中是不能给
* 改变的,所以反射一般只是用来查看此字段的值,而不能修改其值。
* */
//反射私有字段private int age
@Test
public void test13() throws Exception{
Person p = new Person();
Class c = Class.forName("cn.itcast.domain.Person");
Field f = c.getDeclaredField("age");
//需要使用暴力反射
f.setAccessible(true);
f.set(p, 12);
int result = (int) f.get(p);
System.out.println(result);
}
}
3.内省(工程introspector)
在开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API专门用于操作java对象的属性。注意:一个类中的字段不叫属性,但是只要实现了get或set方法的字段就可以叫属性,也就是说具有某个属性是根据set或get方法定的。同时如果只是出现了一个set或get方法而没有定义相关的字段
public void setSex(String sex){this.sex=sex;}
这里的sex也是一个属性。
先看sun公司提供的方法:
先提供实体类:
src下
package cn.itcast.sun;
import java.util.Date;
public class Student {
private String name;
private String password;
private int age;
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
测试类:
package cn.itcast.sun;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.junit.Test;
//使用sun公司提供的方式来操作JavaBean
public class Demo1 {
//通过内省操作name属性
@Test
public void test1() throws Exception{
Student student = new Student();
//得到name属性的属性描述器
PropertyDescriptor pd = new PropertyDescriptor("name", Student.class);
Method method = pd.getWriteMethod();//得到setName方法
method.invoke(student, "Tom");
method = pd.getReadMethod();//得到getName方法
//调用get方法时,如果没有参数就为null
String result = (String) method.invoke(student, null);
System.out.println(result);
}
//操作javaBean的所有属性
@Test
public void test2() throws Exception{
BeanInfo info = Introspector.getBeanInfo(Student.class);
PropertyDescriptor pds[] = info.getPropertyDescriptors();
for(PropertyDescriptor pd : pds){
System.out.println(pd.getName());
}
}
}
说明:但是sun公司提供的这种方式在实际开发中很少用到。
下面看Apache组织提供的方法
Apache提供了一套BeanUtils工具包,专门用于操作bean属性。
实体类
package cn.itcast.beanutils;
import java.util.Date;
public class Student {
private String name;
private String password;
private String email;
private int age;
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
测试类
package cn.itcast.beanutils;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
import org.junit.Test;
public class Demo1 {
//用BeanUtils操作bean
@Test
public void test1() throws Exception{
String name = "Tom";
String password = "111";
String age = "23";
String email = "xxx@sina.com";
String birthday = "1995-09-23";
Student s = new Student();
//对日期我们需要将字符串形式转换为Date类型,有两种方式:
//方式1:使用工具包提供的转换器
ConvertUtils.register(new DateLocaleConverter(), Date.class);
//方式2:自己提供转换器
/*ConvertUtils.register(new Converter() {
//这里注意:Convert接口中只提供了一个抽象方法,我们需要自己实现
@Override
public Object convert(Class type, Object value) {
if(value == null){
return null;
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try{
date = format.parse((String) value);
}catch(ParseException e ){
throw new ConversionException(e);
}
return date;
}
}, Date.class);*/
BeanUtils.setProperty(s, "name", name);
BeanUtils.setProperty(s, "password", password);
//这里注意:此处即使实体类中age是int类型,工具类会自动进行类型转换,但是
//只能转换基本数据类型。
BeanUtils.setProperty(s, "age", age);
BeanUtils.setProperty(s, "email", email);
//Date数据类型需要我们自己进行类型转换
BeanUtils.setProperty(s, "birthday", birthday);
System.out.println(s.getAge());
System.out.println(s.getBirthday());
}
}
说明:这里我们需要导入工具包commons-beanutils-1.9.2.jar,同时导入日志包commons-logging.jar和一个依赖包commons-collections.jar。
4.泛型
泛型的典型应用:
(1)使用迭代器泛型集合中的元素;
(2)使用增强for循环迭代泛型集合中的元素
(3)存取HashMap中的元素;
使用泛型时几个常见的问题:
(1)下面两行代码是错误的:
ArrayList<Integer> list = ArrayList<Object>();
ArrayList< Object > list = ArrayList< Integer >();
因为list虽然被赋值为Object,但是其可使用的方法是由ArrayList决定的,也就是说,在使用泛型的时候一定要保持两边的数据类型相同,同时类型不能是基本数据类型。
(2)下面两行代码是可行的:
ArrayList<Integer> list = ArrayList();
ArrayList< Object > list = ArrayList();
也就是说在使用泛型的时候可以一边使用另一边不使用,这是为了兼容jdk的各个版本。
典型应用
(1)
public static void test1() {
List<Integer>list = new ArrayList<Integer>();
list.add(1);
int i = list.get(0);
}
(2)
public void test2() {
Map<String,Integer>map = new HashMap<String,Integer>();
map.put("aa", 1);
map.put("bb", 2);
Set<Map.Entry<String, Integer>>set = map.entrySet();
for(Map.Entry<String, Integer> me : set){
System.out.println(me.getKey() + "=" + me.getValue());
}
}
注意:我们在遍历map对象时一般都是先提取出实体entry,即转换成一个实体的set集合再遍历,而不是提取key。
自定义泛型:
格式:public static <T> void run(T t);
注意:我们在使用泛型时一定要先定义(static后面就是定义),再使用。
例1(实现数组指定元素交换位置)
public static <T> void swap(T arr[],int pos1,int pos2){
T temp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = temp;
}
例2(将数组颠倒首尾顺序)
public static <T> void reverse(T arr[]){
int start = 0;
int end = arr.length-1;
while(start < end){
T temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
自定义泛型类
若我们自定一个类中有很多方法都使用泛型,同时由于泛型在使用之前必须先定义,那对每一个方法都定义显然很麻烦,这时我们可以在定义类时指定为泛型:
public class Demo3<T>{...}
之后里面的方法在使用泛型时则不需要定义,如:
public void test(){}
但是有一种情况例外,就是静态方法,静态方法使用泛型必须定义:
public static <T> void test(T t){}
因为使用静态方法时我们不需要new类,自然也就没有定义泛型,所以必须自己定义泛型。