5.java基础增强(1)(我的JavaEE笔记)

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类,自然也就没有定义泛型,所以必须自己定义泛型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容