张孝祥java高新技术 --- jkd1.5 新特性 -- 精华总结

简介: 抽象方法的使用如果一个方法中大量出现if语句, 那么, 就应该考虑使用抽象来处理.

1. 抽象方法的使用


 如果一个方法中大量出现if语句, 那么, 就应该考虑使用抽象来处理. 如下例:


package com.lxl;
public class Weekend {
    //周日
    public static Weekend SUN = new Weekend();
    //周一
    public static Weekend MON = new Weekend();
    //周二
    public static Weekend TUE = new Weekend();
    //周三
    public static Weekend WES = new Weekend();
    //周四
    public static Weekend TUR = new Weekend();
    //周五
    public static Weekend FRI = new Weekend();
    //周六
    public static Weekend SAR = new Weekend();
    /*
     * 有一个if语句用来判断, 当前日期的下一个日期是星期几. 
     */
    public Weekend nextDay(Weekend day){
        if(day == SUN) {
            return MON;
        }else if(day == MON){
            return TUE;
        }else if(day == TUE){
            return WES;
        }else if(day == WES){
            return TUR;
        }else if(day == TUR){
            return FRI;
        }else if(day == FRI){
            return SAR;
        }else if(day == SAR){
            return SUN;
        }else{
            return null;
        }
    }
    public static void main(String[] args) {
        Weekend sun = Weekend.SUN;
        sun.nextDay(sun);
    }
}


 在这个方法中, 我定义了一周七天. 如果我想知道明天是星期几, 那么我需要写一个if语句, 大量的 if语句来判断, 明天是星期几. 当程序中出现大量的if语句的时候, 就要想到这样的程序不完美, 需要优化. 比如, 我现在有星期八了, 那么你出来需要添加星期八, 还需要修改if语句.


  使用抽象来代替if语句.是一个好办法.


  修改后的方法如下: 定义了一个抽象方法nextDay


package com.lxl;
public abstract class Weekend {
    //周日
    public static Weekend SUN = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return MON;
        }
    };
    //周一
    public static Weekend MON = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return TUE;
        }
    };
    //周二
    public static Weekend TUE = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return WES;
        }
    };
    //周三
    public static Weekend WES = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return TUR;
        }
    };
    //周四
    public static Weekend TUR = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return FRI;
        }
    };
    //周五
    public static Weekend FRI = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return SAR;
        }
    };
    //周六
    public static Weekend SAR = new Weekend(){
        @Override
        public Weekend nextDay(Weekend day) {
            return SUN;
        }
    };
    /*
     * 当有大量的if语句时, 要考虑如何优化
     */
    /*public Weekend nextDay(Weekend day){
        if(day == SUN) {
            return MON;
        }else if(day == MON){
            return TUE;
        }else if(day == TUE){
            return WES;
        }else if(day == WES){
            return TUR;
        }else if(day == TUR){
            return FRI;
        }else if(day == FRI){
            return SAR;
        }else if(day == SAR){
            return SUN;
        }else{
            return null;
        }
    }*/
    //定义一个抽象方法
    public abstract Weekend nextDay(Weekend day);
    public static void main(String[] args) {
        Weekend sun = Weekend.SUN;
        sun.nextDay(sun);
    }
}


采用抽象方法定义nextDay, 就是可以将if..else转换为独立的类.


  这样做的好处是, 一旦有了星期八, 那么只需要定义星期八这个常量就好了, 不用修改其他地方.


2. 枚举


  • 枚举类的定义


package com.lxl;
public class Weekend2 {
    //这时一个枚举内部类
    public enum Weekend {
        //枚举中每一项,实际上都是这个类的一个子类
        MON, TUE, WEN, THI, FRI, SAT, SUN
    }
    public static void main(String[] args) {
        //1. 选择的时候, 只能在枚举范围内进行选择
        Weekend day = Weekend.MON;
    }
}


  • 重点:1. 枚举类中每一个元素都是这个类的子类. 下面的操作更能说明这一点. 2. 枚举类对可选的对象做了范围限定


  • 枚举类中可用的方法


package com.lxl;
public class Weekend2 {
    //这时一个枚举内部类
    public enum Weekend {
        //枚举中每一项,实际上都是这个类的一个子类
        MON, TUE, WEN, THI, FRI, SAT, SUN
    }
    public static void main(String[] args) {
        //1. 选择的时候, 只能在枚举范围内进行选择
        Weekend day = Weekend.MON;
        //2. 可用的方法
        // 打印名字
        System.out.println(day.name());
        // 序号
        System.out.println(day.ordinal());
        // 将某个字符串转换为枚举类型
        System.out.println(day.valueOf("MON"));
        //获取枚举列表
        System.out.println(Weekend.values().length);
    }
}


运行结果


MON
0
MON
7


  • 为枚举添加构造方法--无参构造方法和有参构造方法


     构造方法必须是私有的


package com.lxl;
public class Weekend2 {
    //这时一个枚举内部类
    public enum Weekend {
        //枚举中每一项,实际上都是这个类的一个子类
        MON(), TUE(1), WEN, THI, FRI, SAT, SUN;
        //3. 为枚举添加构造方法
        private Weekend(){System.out.println("first");}
        private Weekend(int i){System.out.println("second");}
    }
    public static void main(String[] args) {
        //1. 选择的时候, 只能在枚举范围内进行选择
        Weekend day = Weekend.MON;
        //2. 可用的方法
        // 打印名字
        System.out.println(day.name());
        // 序号
        System.out.println(day.ordinal());
        // 将某个字符串转换为枚举类型
        System.out.println(day.valueOf("MON"));
        //获取枚举列表
        System.out.println(Weekend.values().length);
    }
}


输出结果


first
second
first
first
first
first
first
MON
0
MON
7


  • 从结果中可以看出一下几点


  • 构造方法必须写在成员的下面.


  • 枚举的构造方法必须是private的


  • 枚举的每一个成员都是枚举的子类


  • 只要调用枚举类, 就会初始化枚举子类, 子类又会调用父类的构造方法.


  • 每一个枚举类的子类默认调用的是枚举类的无参构造方法.


  • 调用枚举的有参构造方法,可以使用"子类名(参数)"的形式

 

  • 为枚举添加抽象方法, 下面是一个交通信号灯亮的顺序枚举类


package com.lxl;
public class EnumTest3 {
    //交通信号灯
    public enum TrafficLamp{
        RED {
            @Override
            public TrafficLamp nextLamp() {
                return YELLOW;
            }
        }, 
        YELLOW {
            @Override
            public TrafficLamp nextLamp() {
                return BLUE;
            }
        }, 
        BLUE {
            @Override
            public TrafficLamp nextLamp() {
                return RED;
            }
        };
        //下一个亮的信号灯--抽象方法
        public abstract TrafficLamp nextLamp();
    } 
    public static void main(String[] args) {
        TrafficLamp red = TrafficLamp.RED;
        System.out.println(red.nextLamp());
    }
}


运行结果:


YELLOW


  • 从这个demo可以得出以下结论:


  • 枚举中可以定义抽象方法


  • 进一步说明, 每一个枚举的成员都是枚举的子类, 子类必须实现父类的抽象方法.

 

  • 为上一个交通信号灯案例添加时间. 这个时间我们可以放在构造方法中.


package com.lxl;
public class EnumTest3 {
    //交通信号灯
    public enum TrafficLamp{
        RED(30) {
            @Override
            public TrafficLamp nextLamp() {
                return YELLOW;
            }
        }, 
        YELLOW(45) {
            @Override
            public TrafficLamp nextLamp() {
                return BLUE;
            }
        }, 
        BLUE(5) {
            @Override
            public TrafficLamp nextLamp() {
                return RED;
            }
        };
        //下一个亮的信号灯--抽象方法
        public abstract TrafficLamp nextLamp();
        public int time;
        private TrafficLamp(int time){
            this.time = time;
        }
public String toString(){
              return this.name() + " 亮灯时间: " + time;
          }
    } 
    public static void main(String[] args) {
        TrafficLamp red = TrafficLamp.RED;
        System.out.println(red.nextLamp());
    }
}


运行结果


YELLOW 亮灯时间: 45


  • 结论


  • 子类调用父类的构造方法。


  • 每一个枚举成员都是父类的一个子类。

 

  • 如果枚举类中只有一个元素, 那么这个枚举可以看做一个单例的实现方法。


3. 反射


  • 内存中同一个类只有一份字节码


String str = "abc";
Class cla1 = str.getClass();
Class cla2 = String.class;
Class cla3 = Class.forName("java.lang.String");
/*
 * 同一份字节码, 在内存中只有一份
 */
System.out.println(cla1 == cla2 );
System.out.println(cla2 == cla3);
/*
 * 判断,一个类型是否是基本类型. 基本类型有9个: 8个基本类型+void.
 * int long short float double char byte boolean 
 */
//String 不是基本类型
System.out.println(str.getClass().isPrimitive());
//int 是基本类型
System.out.println(int.class.isPrimitive());
//Integer 不是基本类型
System.out.println(Integer.class.isPrimitive());
//int 和 Integer 是不同的类型, 他们在内存中的字节码是不同的
System.out.println(int.class == Integer.class);
//Integer.Type方法返回的是基本类型int的字节码
System.out.println(Integer.TYPE == int.class);
//数组也是一个Class对象
//int[]数组不是基本类型
System.out.println(int[].class.isPrimitive());
//int[] 数组是数组么? 是的
System.out.println(int[].class.isArray());


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


  • Class.class(); 类名.class();


  • new Date().getClass()


  • Class.forName("类的全限定名")


  • 九个预定义的class实例对象


  • 参考Class类的isPrimitive方法.


  • 8个基本类型+void


  • int , long, short, double, float, char, byte, bollean, 以及void


  • Constructor类


//获得String这个类的所有个构造方法
Constructor<?>[] cons = String.class.getConstructors();
//获得String的指定构造方法: String(StringBuffer s){}
//下面表示只接受一个参数StringBuffer类型的构造方法
Constructor<?> con = String.class.getConstructor(StringBuffer.class);
//将构造方法实例化
Object o = con.newInstance(new StringBuffer("abc"));
System.out.println(o);


我记得 之前说过, 得到Class类以后, 可以调用Class.newInstances()


/*
 * 实例化
 */
Class cla4 = Class.forName("java.lang.String");
//我们可以直接使用Class的newInstance()方法. 但注意这个方法是无参的构造方法.
Object o4 = cla4.newInstance();
/*
 * 如果调用一个类的有参构造方法呢?
 * 使用构造器. 如下操作, 就是调用了含有一个StringBuffer类型参数的构造方法
 * 这样调用的就是有参的构造方法
 */
Constructor<?> con4 = cla4.getConstructor(StringBuffer.class);
Object o44 = con4.newInstance(new StringBuffer("aaa"));
System.out.println(o44);


Field


如何获取共有字段


public class RefelectPoint {
    private int x;
    public int y;
    public RefelectPoint(int x, int y){
        this.x = x;
        this.y =y;
    }
}


下面的代码在main方法中执行


/**
 * 字段
 * 如何获取共有字段
 */
RefelectPoint rp = new RefelectPoint(4,6);
//现在我要通过反射获取rp对象中的x字段的值和y字段的值
Field fieldy = rp.getClass().getField("y");
/*
 * 注意: 这里的fieldx表示的是字段, 他不代表任何值. 因为RefelectPoint有很多歌对象, 
 * fieldx仅表示这些对象中的指定字段. 那到底是哪个对象的值呢? 也就是如何获取这个值呢?
 * 
 * 注意, 使用这种方法只能获得public域的参数
 */
System.out.println(fieldy.get(rp));


如何获取私有字段


/*
 * 那如何获取private类型的参数呢
 * 如果直接获取会报告异常:   java.lang.NoSuchFieldException: x
 * 
 * 我们可以通过对象的getDeclareField("x")来获取.
 * 这个时候, 我们调用这个方法, 返回的依然是一个异常 :modifiers "private"
 * 这时我们要调用setAccessible(true)强制通知,告诉编译器我可以获取这个私有属性
 */
Field fieldx = rp.getClass().getDeclaredField("x");
fieldx.setAccessible(true);
System.out.println(fieldx.get(rp));


练习: 将一个对象中的所有String类型的成员变量的值中的b改成a


/**
 * 将一个对象中的所有String类型的成员变量的值中的b改成a
 * 思路: 
 * 1. 获取所有的String类型的成员
 * 2. 获取成员对应的值
 * 3. 通过正则表达式或者replaceAll方法对字符串进行替换
 */
Class cla5 = RefelectPoint.class;
Constructor<?> cons5 = cla5.getConstructor(int.class, int.class);
Object o5 = cons5.newInstance(5, 10);
Field[] f5s = cla5.getFields();
for(Field f5: f5s){
    //获取Field的类型
    Class<?> t5 = f5.getType();
    //判断是否是String类型,只需看他的字节码是否是同一份就可以了.
    if(t5 == String.class){
        String v5 = (String) f5.get(o5);
        v5 = v5.replaceAll("b", "a");
        f5.set(o5, v5);
        System.out.println(v5);
    }
}


运行结果:


aall
aasketaall
itcas 


Method


  • 如何获取一个类的指定方法呢


/**
 * 方法反射
 */
String str1 = "abc";
//第一步:通过反射调用指定方法
Method method = String.class.getMethod("charAt", int.class);
//第二步: 执行方法
char c = (char) method.invoke(str1, 2);
System.out.println(c);  


  • 通过反射调用getMethod()方法,在执行invoke方法即可。 如果想获取所有的方法, 可以使用getMethods()方法。


  • 如果在使用Method.invoke(参数1,参数2): 如果第一个参数是null,则表示这是调用的一个静态方法。
相关文章
|
21天前
|
安全 Java 数据安全/隐私保护
|
21天前
|
搜索推荐 Java
Java的面向对象特性主要包括封装、继承和多态
【4月更文挑战第5天】Java的面向对象特性主要包括封装、继承和多态
15 3
|
1月前
|
人工智能 Java 编译器
Java 19的未来:新特性、性能优化和更多
Java 19的未来:新特性、性能优化和更多
|
1月前
|
Java API 数据处理
Java 8新特性之Stream API详解
【2月更文挑战第22天】本文将深入探讨Java 8中引入的Stream API,这是一种基于函数式编程的新特性,用于处理集合数据。我们将详细介绍Stream的基本概念、操作方法以及在实际开发中的应用,帮助读者更好地理解和使用这一强大的工具。
|
1天前
|
安全 Java 大数据
探索Java的奇妙世界:语言特性与实际应用
探索Java的奇妙世界:语言特性与实际应用
|
1天前
|
Java
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
5 0
|
7天前
|
机器学习/深度学习 Java API
Java8中的新特性
Java8中的新特性
|
10天前
|
分布式计算 Java API
Java 8新特性之Lambda表达式与Stream API
【4月更文挑战第16天】本文将介绍Java 8中的两个重要新特性:Lambda表达式和Stream API。Lambda表达式是Java 8中引入的一种新的编程语法,它允许我们将函数作为参数传递给其他方法,从而使代码更加简洁、易读。Stream API是Java 8中引入的一种新的数据处理方式,它允许我们以声明式的方式处理数据,从而使代码更加简洁、高效。本文将通过实例代码详细讲解这两个新特性的使用方法和优势。
|
17天前
|
Java API 开发者
Java 8新特性之函数式编程实战
【4月更文挑战第9天】本文将深入探讨Java 8的新特性之一——函数式编程,通过实例演示如何运用Lambda表达式、Stream API等技术,提高代码的简洁性和执行效率。
|
17天前
|
存储 Java API
java8新特性 lambda表达式、Stream、Optional
java8新特性 lambda表达式、Stream、Optional