2022.0704学习Java笔记之Java新特性

所谓新特性是指从JDK1.5之后开始的

可变参数

  如果说现在有这样一个要求,要求实现整数的加法操作,并且方法可以接受任意多个整形数据一起实现加法操作,如果按照传统思路,现在实现方式可以采用数组作为参数的类型。

package cn.mldn.demo;

public class TextDemo {
    public static void main(String args[]){
      System.out.println(add(new int[] {1,2,3}));
      System.out.println(add(new int[] {1,2,3,4}));
    }
    public static int add (int data[]){
        int sum = 0;
        for(int x=0;x<data.length;x++){
            sum = sum + data[x];
        }
        return sum;
    }
}

  此处使用数组完全属于无奈,虽然实现了操作,但题目本身要求,可以接收任意多个整形数据,每一个数据之间应该使用“,”分隔才最合适,所以为解决此问题,自JDK1.5之后增加了可变参数方法的定义

[ public | protected | private ] [ Final  ] [ static ] 返回值类型 方法名称(参数类型 ... 参数名称)[ throws 异常,异常 ...] {

        [ return [ 返回值 ] ; ] 

}

虽然可变数组语法形式上有些别扭,但是可以把它当作数组来理解

范例:使用可变参数

package cn.mldn.demo;

public class TextDemo {
    public static void main(String args[]){
      System.out.println(add(new int[] {1,2,3}));
      System.out.println(add(new int[] {1,2,3,4}));
      System.out.println(add(1,2,3,4));
    }
    public static int add (int ... data){
        int sum = 0;
        for(int x=0;x<data.length;x++){
            sum = sum + data[x];
        }
        return sum;
    }
}

  可变参数的最大好处在于方法调用时,可以随意编写参数进行数据的传递,但是此类操作只会在系统类库的学习中遇见

foreach输出

如果说现在有一个数组要实现数据的输出,一定使用for循环完成

例如:使用for循环输出一个数组

package cn.mldn.demo;

public class TextDemo {
    public static void main(String args[]){
        int data[] = new int [] {1,2,3,4};
        for (int x = 0;x< data.length ; x++){
            System.out.println(data[x]);
        }
    }
}

而从JDK1.5之后增加了一类的输出结构

for (数据类型 变量 :数组|集合){

        //操作代码;

}

  本程序的意思是根据数组的长度进行循环操作,并且每次循环的时候取出数组之中的每一个元素,将其赋值给生命的变量

范例:使用foreach输出

package cn.mldn.demo;

public class TextDemo {
    public static void main(String args[]){
        int data[] = new int [] {1,2,3,4};
        for (int x :data){
            System.out.println(x);
        }
    }
}

静态导入

首先我们来观察一个全部是static方法的类

package cn.mldn.util;

public class MyMath {
    public static int add (int x, int y){
        return x+y;
    }
    public static int sub (int x,int y){
        return x-y;
    }
}

如果在传统情况下,要使用以上类之中所定义的方法,应该先导入包.类,而后通过类名称调用

package cn.mldn.demo;
import cn.mldn .util.MyMath;
public class TextDemo {
    public static void main(String args[]){
        System.out.println(MyMath.add(10,20));
        System.out.println(MyMath.sub(50,20));
    }
}

  在之前学习过,如果在主类中定义的方法,并且由主方法直接调用的话,则方法上必须使用static,那么能不能把以上导入包中的static方法不使用使用类名称世界调用呢(相当于将方法直接定义在主类之中)

范例:静态导入

package cn.mldn.demo;
import static cn.mldn .util.MyMath.* ;
public class TextDemo {
    public static void main(String[] args){
        System.out.println(add(10,20));
        System.out.println(sub(50,20));
    }
}

此时调用方法不再加入类名称,就好比这些方法定义在主方法中一样

泛型的引出

现在要求用户设计一个表示坐标轴的类,但是次坐标保存数据有可能有如下几种情况

  • 整型数据:x = 10,  y = 30
  • 小数数据:x =10.4,y = 20.9
  • 字符串数据:x = 东经100度,y = 北纬20度

  那么现在很明显,Point类表示坐标,那么针对于坐标点有三类数据,而在Point类里面应该有两个属性 x、y ,所以现在首先要解决的问题就是确定出x、y属性的数据类型

  既然现在要保存有int、double、String或者以后有可能的其他类型,则自然想到使用Object,因为存在如下转换关系

  • 保存整型:int→ 自动装箱为 Integer→ 向上转型为Object;
  • 保存小数:double→ 自动装箱为Double→向上转型为Object;
  • 保存字符串:String→向上转型为Object。

范例,实现代码

package cn.mldn.demo;

class Point{
    private Object x;
    private Object y;
    public void setX(Object x ){
        this.x = x;
    }
    public void setY(Object y){
        this.y = y;
    }
    public Object getX(){
        return x;
    }
    public Object getY(){
        return y;
    }
}
public class TextDemo {
    public static void main(String[] args){
        //第一层次:设置坐标数据
        Point point = new Point() ;
        point.setX(10) ;      //向上转型为Object
        point.setY(30) ;      //向上转型为Object
        //第二层次:取得做坐标数据
        int x = (Integer) point.getX() ;      //向下转型
        int y = (Integer) point.getY() ;      //向下转型
        System.out.println("x =" + x + ", y =" + y);
    }
}

   此时代码已经完成要求,但是本程序依靠的是Object可以接收所有数据类型这一特征展开的,但Object范围太大,那么在程序运行过程中就可能出错。

范例:观察问题

public class TextDemo {
    public static void main(String[] args){
        //第一层次:设置坐标数据
        Point point = new Point() ;
        point.setX(东经100度) ;      //向上转型为Object
        point.setY(30) ;      //向上转型为Object
        //第二层次:取得做坐标数据
        int x = (Integer) point.getX() ;      //向下转型
        int y = (Integer) point.getY() ;      //向下转型
        System.out.println("x =" + x + ", y =" + y);
    }

  此时程序编译的时候没有出现任何错误,但是执行的时候就会出现“ClassCastException”,所以来讲此时程序就存在有安全隐患,事实上,所有的向下转型都可能存在这种安全隐患,最好的做法就是不转型。

  而在JDK1.5之后由于引进了泛型的处理机制,所以此类问题很好解决了,所谓泛型指的就是类之中定义的属性,在程序编译时不会给出具体的类型,只给处一个类型的占位标记,而后在使用此类产生对象时,在设置具体类型

范例:利用泛型修改

package cn.mldn.demo;

class Point<T>{         //T只是一个标记,并不是类型
    private T x;
    private T y;
    public void setX(T x ){
        this.x = x;
    }
    public void setY(T y){
        this.y = y;
    }
    public T getX(){
        return x;
    }
    public T getY(){
        return y;
    }
}
public class TextDemo {
    public static void main(String[] args){
        //第一层次:设置坐标数据
        Point<String> point = new Point<String>() ;
        point.setX("东经100度") ;
        point.setY("北纬20度") ;
        //第二层次:取得做坐标数据
        String x = point.getX() ;      //向下转型
        String y = point.getY() ;      //向下转型
        System.out.println("x = " + x + ", y = " + y);
    }
}

  那么此时由于泛型技术的出现,取消了向下转型,而向下转型的取消就相当与消除了所有的“ClassCastException”这种安全隐患,但需要注意的是,在设置泛型类型的时候只能够使用引用数据类型,即:如果要保存int或者double,用包装类操作

范例:保存int

public class TextDemo {
    public static void main(String[] args){
        //第一层次:设置坐标数据
        Point<Integer> point = new Point<Integer>() ;
        point.setX(10) ;
        point.setY(20) ;
        //第二层次:取得做坐标数据
        int x = point.getX() ;
        int y = point.getY() ;
        System.out.println("x = " + x + ", y = " + y);
    }
}

  但是为了保证以前的代码没有任何错误,所以如果在使用泛型标记类的时候没有设置泛型类型,那么会按照Object做默认处理

public class TextDemo {
    public static void main(String[] args){
        //第一层次:设置坐标数据
        Point point = new Point() ;
        point.setX(10) ;
        point.setY(20) ;
        //第二层次:取得做坐标数据
        int x = (Integer)point.getX() ;
        int y = (Integer)point.getY() ;
        System.out.println("x = " + x + ", y = " + y);
    }
}

所以不设置泛型请一定要记住,类型就是Object

通配符

为了方便,重新定义一个泛型的类并且针对于Info类实现一次引用传递

package cn.mldn.demo;

class Info<T>{
    private T msg;
    public void setMsg(T msg){
        this.msg = msg;
    }
    public T getMsg(){
        return msg;
    }
}
public class TextDemo {
    public static void main(String[] args){
        Info<String> info = new Info<String>() ;
        info.setMsg("Hello world!") ;
        fun(info);
    }
    public static void fun(Info<String> temp){
        System.out.println(temp.getMsg());
    }
}

  既然是设置泛型,肯定不可能只是String一种,有可能设置其他类型,此时如果传递的是一个“Info<Integer>”那么fun()方法是不可能使用的。用重载也不可能,因为重载看重的是数据类型,于是发现,一旦使用泛型,在之前好不容易解决的参数统一问题又出来了,但是如果参数上不设置泛型,那么就表示的类型就是Object

范例:参数上不使用泛型

public class TextDemo {
    public static void main(String[] args){
        Info<String> info = new Info<String>() ;
        info.setMsg("Hello world!") ;
        fun(info);
    }
    public static void fun(Info temp){
        temp.setMsg(100);       //现在修改为Integer
        System.out.println(temp.getMsg());
    }
}

  所以这个时候发现,不设置泛型,操作的数据形式就可能出现混乱,于是现在就可以总结出来,我们需要一个可以接收任意任意的泛型类型,但是又不能修改里面的数据,并且可以取得里面数据的操作。那么现在就只能够利用通配符“?”来完成此功能。

范例:使用“?”解决问题

public class TextDemo {
    public static void main(String[] args){
        Info<String> info = new Info<String>() ;
        info.setMsg("Hello world!") ;
        fun(info);
    }
    public static void fun(Info<?> temp){
        System.out.println(temp.getMsg());
    }
}

但是在“?”的基础上又扩充了两个子的通配符:

  • 设置泛型上线:?extends 类(Textends 类);例如:“T extends Number”,表示此处只能够设置Number和Number的子类(Integer);
  • 设置泛型下线:?super String,表示只能够设置String或者String的父类Object。

范例:设置泛型上线

package cn.mldn.demo;

class Info<T extends Number>{
    private T msg;
    public void setMsg(T msg){
        this.msg = msg;
    }
    public T getMsg(){
        return msg;
    }
}
public class TextDemo {
    public static void main(String[] args){
        Info<Integer> info = new Info<Integer>() ;
        info.setMsg(100) ;
        fun(info);
    }
    public static void fun(Info<? extends Number> temp){
        System.out.println(temp.getMsg());
    }
}

范例:设置泛型下线

package cn.mldn.demo;

class Info<T>{
    private T msg;
    public void setMsg(T msg){
        this.msg = msg;
    }
    public T getMsg(){
        return msg;
    }
}
public class TextDemo {
    public static void main(String[] args){
        Info<String> info = new Info<String>() ;
        info.setMsg("Hello world!") ;
        fun(info);
    }
    public static void fun(Info<? super String> temp){
        System.out.println(temp.getMsg());
    }
}

 泛型接口

在之前所创建的泛型都是在类的定义上使用的,那么也可以在接口上使用泛型。

范例:在接口上使用泛型

interface Message<T>{
    public void print(T msg);
}

那么此时对于这样的接口有两种实现方式:

方式一:在子类实现接口后继续使用泛型

package cn.mldn.demo;

interface Message<T>{
    public void print(T msg);
}
class MessageImpl<T> implements Message<T>{
   public void print(T msg){
       System.out.println(msg);
   }
}
public class TextDemo {
    public static void main(String[] args){
        Message<String> message = new MessageImpl<String>();
        message.print("Hello World");
    }
}

方式二:子类直接设置具体的泛型类型

package cn.mldn.demo;

interface Message<T>{
    public void print(T msg);
}
class MessageImpl implements Message<String>{
   public void print(String msg){
       System.out.println(msg);
   }
}
public class TextDemo {
    public static void main(String[] args){
        Message<String> message = new MessageImpl();
        message.print("Hello World");
    }
}

泛型方法

  在之前已经接触过泛型方法,但是所有接触到的泛型方法都是声明在泛型类之中,而现在所讲泛型方法指的是没有泛型声明的类上定义的方法。

范例:定义泛型方法

public class TextDemo {
    public static void main(String[] args){
        Integer temp[] = fun(1,2,3);
        for(Integer x : temp){
            System.out.println(x);
        }
    }
    public static <T> T[] fun(T ... args){
        return args;
    }
}

枚举

  多例设计模式典型特点:构造方法私有化,而后在类的内部实例化好若干个对象,并且通过static方法返回,可是如果按照之前编写的多礼设计实际上是存在问题的。

范例:观察问题

package cn.mldn.demo;

class Color{
    private String title;
    private static final Color RED = new Color("红色");
    private static final Color GREEN = new Color("绿色");
    private static final Color BLUE = new Color("蓝色");
    private Color(String title){
        this.title = title;
    }
    public String toString(){
        return this.title;
    }
    public static Color getInstance(int ch){
        switch(ch){
            case 0 :
                return RED;
            case 1 :
                return GREEN;
            case 2 :
                return BLUE;
            default :
                return null;
        }
    }
}
public class TextDemo {
    public static void main(String[] args){
        Color red = Color.getInstance(5);
        System.out.println(red);
    }
}

  在之前的代码之中,实现的多例设计模式里面存在一个矛盾,发现用户实际上并不知道类中存在有多少个可用对象,而且所有的对象必须通过一个static方法,传入一些数字来取得。那么就证明传统的多例设计并不好用。所以为了解决这样的问题,从JDK1.5开始增加了一个enum的关键字,使用它可以定义枚举。
范例:定义枚举

enum Color{
    RED,GREEN,BLUE;
}

枚举之中的对象都是使用String final定义的,所以一定要用大写字母表示

范例:使用枚举

package cn.mldn.demo;

enum Color{
    RED,GREEN,BLUE;
}
public class TextDemo {
    public static void main(String[] args){
        Color red = Color.RED;
        System.out.println(red);
    }
}

此时可以清楚发现,直接利用枚举对象就可以取得里面保存的对象信息,同时在枚举操作的过程里面还可以取得全部对象

范例:输出枚举全部内容

package cn.mldn.demo;

enum Color{
    RED,GREEN,BLUE;
}
public class TextDemo {
    public static void main(String[] args){
        for(Color c : Color.values()) {
            System.out.println(c);
        }
    }
}

通过演示应该清楚了enum关键字的作用,但是严格来讲,在Java之中,使用enum关键字定义的枚举对象就严格来讲相当于是一个使用class定义的类,而后继承了Enum类是一样的。首先Enum是一个抽象类,在Enum类之中定义了如下方法

  • 构造方法:protected Enum(String name,int ordinal),构造被封装,子类可以调用;
  • 取得枚举名字:public final String name();
  • 取得索引号:public final int ordinal()。

范例:调用Enum类的方法

package cn.mldn.demo;

enum Color{
    RED,GREEN,BLUE;
}
public class TextDemo {
    public static void main(String[] args){
        for(Color c : Color.values()) {
            System.out.println(c.name() + "," + c.ordinal());
        }
    }
}

enum与Enum的区别

  • enum是一个关键字,而Enum是一个抽象类
  • 使用enum定义的结构就相当于一个类继承了Enum类

在枚举之中定义其他结构

虽然说枚举是单例设计模式的实现,但是通过研究多例设计模式,你可以清楚的发现,多例设计模式之中,除了定义类对象之外,实际上还可以构造方法、普通方法、属性。而枚举也可以做到这些,但是有如下要求:

  • 枚举之中定义多个结构的时候,枚举的对象要写在类第一行;
  • 枚举毕竟属于多例设计,构造方法绝对不能使用public表示。

范例:在枚举之中定义其他结构

package cn.mldn.demo;

enum Color{
    RED("红色"),GREEN("绿色"),BLUE("蓝色");   //对象
    private String title;
    private Color(String title){
        this.title = title;
    }
    public String toString(){
        return this.title;
    }
}

 而且除了以上结构之外,枚举作为一个特殊类,也可以让其实现接口

范例:让枚举实现接口

package cn.mldn.demo;

interface Message{
    public String getInfo();
}
enum Color{
    RED("红色"),GREEN("绿色"),BLUE("蓝色");   //对象
    private String title;
    private Color(String title){
        this.title = title;
    }
    public String toString(){
        return this.title;
    }
    public String getInfo(){
        return this.title;
    }
}
public class TextDemo {
    public static void main(String[] args){
        for(Color c : Color.values()) {
            System.out.println(c);
        }
    }
}

Annotation

如果要想彻底理解 Annotation 必须通过开发的结构的历史做一个回顾

  • 最早的时候编写代码,会将所有与系统配置的信息都写在程序代码之中;
  • 随后产生了新的设计,使用一些配置文件保存用户可修改的配置信息,通过程序读取配置数据,这种开发模式虽然适合于没有意义的用户维护,但是开发太麻烦了。
  • 第三个阶段:是将配置文件信息写回到程序之中,但是必须与原始程序做一个有效的分割,于是有了注解的方式(编译时的注解、运行时的注解、类级注解)。

在Java之中已经为用户提供好了三类注解:@Override、@Deprecated、@SuppressWranings。

1、准确的覆写:@Override

  覆写定义:方法名称、参数类型及个数、返回值类型全部相同,而且被覆写的方法不能拥有比父类更为严格的访问控制权限。但是在实际的开发之中,有可能因为用户的手误,会写出这样的代码。

package cn.mldn.demo;

class Message{
    @Override 
    public String toString(){   //这不是覆写
        return "这是一个消息";
    }
}
public class TextDemo {
    public static void main(String[] args){
       System.out.println(new Message());
    }
}

可以在一个方法覆写时加上一个@Override,表示这个方法是覆写父类的方法

2、声明过期操作:@Deprecated
  在一个系统的开发之中,一定都是不断进行完善的,那么就可能出现这样情况,例如:用户在一个系统的1.0版本提供了一个Message的类,但是这个类之中最早有了一个fun()方法,后来到了系统的3.0时代,发现Mesfun类最早设计的fun()方法有可能会存在安全隐患,于是提供了一个新的funNew()方法。但是这个时候就面临一个问题:部分的开发者已经利用了Message类编写了许多程序,如果新版本直接废除掉fun(),会导致版本更新的异常。现在应该告诉新用户这个方法不能使用了,但是如果是已经能够正常使用的老系统,那么就照旧保留即可。。
范例:声明过期操作。

package cn.mldn.demo;

class Message{
    @Deprecated
    public void fun(){   //这不是覆写
        System.out.println("Hello World");
    }
}
public class TextDemo {
    public static void main(String[] args){
       new Message().fun() ;
    }
}

3.压制警告信息 @SuppressWranings

警告不是语法错误,只能说明用户的操作过程中出现不当,有可能会对以后产生问题

package cn.mldn.demo;

class Message<T>{
    public void fun(){   //这不是覆写
        System.out.println("Hello World");
    }
}
public class TextDemo {
    @SuppressWarnings({"rawtypes" , "unused"})
    public static void main(String[] args){
        Message msg = new Message();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值