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

摘要:
1.抽象方法的使用如果方法中有大量If语句,则应考虑使用抽象方法。例如:packagecom.lxl;PublicclassWeekend{//Sunday publicstaticWeekendSUN=newWeekend();//Monday publicstaticWeekendMON=newWeekend()

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,则表示这是调用的一个静态方法。

   

免责声明:文章转载自《张孝祥java高新技术 --- jkd1.5 新特性 -- 精华总结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇postman笔记1--postman插件安装教程STM32组合设备实现USB转双串口下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C# 通过ServiceStack 操作Redis——Hash类型的使用及示例

接着上一篇,下面转到hash类型的代码使用 Hash:结构 key-key-value,通过索引快速定位到指定元素的,可直接修改某个字段 /// <summary> /// Hash:类似dictionary,通过索引快速定位到指定元素的,耗时均等,跟string的区别在于不用反序列化,直接修改某个字段 /// str...

微信公众平台获取用户openid

首先需要一个域名,如花生壳域名,然后在微信公众平台配置,注意,正式环境下必须要备案好了的域名,测试环境下没有关系,先公众号功能设置:》接口权限中的网页授权获取用户基本信息》注册一个测试者账号,进行设置,也要修改网页授权获取用户基本信息 public ActionResult Index(string id, string code, string st...

C++20新特性

C++20新特性 新增关键字(keywords) concept requires constinit consteval co_await co_return co_yield char8_t 模块(Modules) 优点: 1)没有头文件; 2)声明实现仍然可分离, 但非必要; 3)可以显式指定导出哪些类或函数; 4)不需要头文件重复引入宏 (incl...

Java操作PDF,在PDF文件指定位置输出水印

需要参考我的上一篇博客,定位PDF中的关键字,找出需要打印水印的坐标位置。 先说测试结果(PDF原件也是上一篇中的图片所示): 新生成的带有水印的PDF文件如下所示: junit测试代码及输出: maven配置文件 <!--引入pdf --> <dependency> <groupId>com.i...

Unity3D中使用委托和事件

Unity3D中使用委托和事件  c#语言规范 阅读目录 1.C#中的委托、事件引入 2.方法的参数是方法 前言: 本来早就想写写和代码设计相关的东西了,以前做2DX的时候就有过写写观察者设计模式的想法,但是实践不多。现在转到U3D的怀抱中,倒是接触了不少委托事件的写法,那干脆就在此总结一下吧。 回到目录 1.C#中的委托、事件引入 本想去找...

C# Dictionary通过value获取对应的key值[转发]

1:最直白的循环遍历方法,可以分为遍历key--value键值对以及所有的key两种表现形式 2:用Linq的方式去查询(当然了这里要添加对应的命名空间 using System.Linq)  如下为一个十分简单的代码示例: private void GetDicKeyByValue() { Dictionary...