jdk新特性

1.9对集合添加的优化java 9添加了几种集合工厂的方式,更方便创建少量元素的集合,map实例/新的list,set,Map的静态工厂方法可以更方便的创建集合的不可变实例.list接口 set接口 map接口 增加一个静态方法 of 可以给集合一次性添加多个元素使用的前提:当集合中储存的元素的个数已经确定了,不在改变时使用.注意:1.of方法只适用于list set map 接口 不适用与接口的实现类2.of方法的返回值是一个不能改变的集合,集合不能再使用add put 方法添加元素
摘要由CSDN通过智能技术生成

1.9对集合添加的优化

java 9添加了几种集合工厂的方式,更方便创建少量元素的集合,map实例/新的list,set,Map的静态工厂方法可以更方便的创建集合的不可变实例.

list接口 set接口 map接口 增加一个静态方法 of 可以给集合一次性添加多个元素

使用的前提:当集合中储存的元素的个数已经确定了,不在改变时使用.

注意:

1.of方法只适用于list set map 接口 不适用与接口的实现类

2.of方法的返回值是一个不能改变的集合,集合不能再使用add put 方法添加元素,会抛出异常.

3.set与map接口在调用of方法的时候,不能有重复的元素 ,否则抛出异常.

例子

public class Gather {
   
    public static void main(String[] args) {
   
        List<String> list = List.of("1","11","111");
//        list.add("11");//UnsupportedOperationException 不支持操作异常
        System.out.println(list);

       // Set.of("2", "1", "3","3");//IllegalArgumentException 参数异常
        Set<String> strings = Set.of("2", "1", "3");
        //strings.add("11");//UnsupportedOperationException 不支持操作异常
        System.out.println(strings);

       //Map.of(1, "2", 1, "3");//IllegalArgumentException 参数异常
        Map<Integer, String> integerStringMap = Map.of(1, "2", 2, "3");
       // integerStringMap.put(3,"3");//UnsupportedOperationException 不支持操作异常
        System.out.println(integerStringMap);

        //map也可以这样添加 同样是不可变集合
        Map<Integer, String> map = Map.ofEntries(Map.entry(1, "2"),Map.entry(2,"3"));
        System.out.println(map);
        //map.put(3,"200");

        //扩展 在java8之前的版本也可以这样创建不可变集合
        //List
        List<String> list1 = Arrays.asList("1", "2");
//        list1.add("1");报错

        List<String> list2=new ArrayList<>();
        list2.add("1");
        List<String> list3 = Collections.unmodifiableList(list2);//将集合变成不可变集合
        //System.out.println(list3);
        //list3.add("1");报错

        //Set
        Set<String> stringSet=new HashSet<>();
        stringSet.add("2");
        Set<String> stringSet1 = Collections.unmodifiableSet(stringSet);
        //stringSet1.add("1");报错


        //map
        Map<String,Integer> map1=new HashMap<>();
        map1.put("1",1);
        Map<String, Integer> map2 = Collections.unmodifiableMap(map1);
        //map2.put("2",2);报错


    }
}

详情代码E:\后端修炼之路\jdk新特性\jdk\src\jdk\nine\Gather.java

idea的debug调试

f8:逐行执行程序

f7:进入到方法中

shift+f8:跳出方法

f9:跳到下一个断点,如果没有下一个断点,那么就结束程序.

ctrl+f2:退出debug模式,停止程序

console:切换到控制台

LinkedHashMap集合

示例:

public class TestLinkedHashMap {
   
    public static void main(String[] args) {
   
        HashMap<String,String> map=new HashMap<>();
        map.put("t","t");
        map.put("b","b");
        map.put("s","s");
        map.put("c","c");
        map.put("d","d");
        System.out.println(map);//key不允许重复,无序
        LinkedHashMap<String,String> hashMap=new LinkedHashMap();
        hashMap.put("t","t");
        hashMap.put("b","b");
        hashMap.put("s","s");
        hashMap.put("c","c");
        hashMap.put("d","d");
        System.out.println(hashMap);//key不允许重复有序 与放进去的循序一样

    }
}

1.8中的Lambda表达式

比如实现接口线程的方式

使用匿名内部类

new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                System.out.println("线程一");
            }
        }).start();

一方面,匿名内部类可以帮我们省去实现类的定义;另一方面 匿名内部类的语法 确实太复杂了

同样的例子在 Lambda中 更加简单

例子

  new Thread(()->{
   
            System.out.println("线程二");
        }).start();

lambda表达式的标准格式:

由三部分组成:

a.一些参数

b.一个箭头

c.一段代码

格式:

(参数列表)->{一些重写方法的代码}

解释说明格式:

():接口中抽象方法的参数,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔.

->:传递的意思,把参数传递给方法体{}

{}:重写接口的抽象方法的方法体

例子

无参数无返回值

public class TestLamdba {
   
    public static void main(String[] args) {
   
   tempWrite(new Temp() {
   
            @Override
            public void write() {
   
                System.out.println("使用内部类重写");
            }
        });

        tempWrite(()->{
   
            System.out.println("使用lamdba重写");
        });
    }
    public static void tempWrite(Temp temp){
   
        temp.write();
    }
 }
interface Temp{
   
    void write();
}

有参数有返回值

class Person{
   
    public int age;
    public String name;
    public Person(int age, String name) {
   
        this.age = age;
        this.name = name;
    }
    public Person() {
   
    }
    @Override
    public String toString() {
   
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    
    public class TestLamdba {
   
    public static void main(String[] args) {
   
             Person[] persons={
   new Person(18,"张三"),new Person(13,"小妹妹"),new Person(14,"小弟弟")};
        
        Arrays.sort(persons, new Comparator<Person>() {
   
            @Override
            public int compare(Person o1, Person o2) {
   
                return o1.age-o2.age;
            }
        });//使用内部类的方式进行重写 接口 制定比较规则


        Arrays.sort(persons,(Person o1, Person o2)->{
   
               return o1.age-o2.age;
        });//使用lamdba重写

        for (Person ps:persons) {
   
               System.out.println(ps);
        }

    }
  }

lambda表达式的简略写法

lambda表达式是可推导,可以省略

凡是根据上下文推导出来的内容,都可以省略不写

可以省略的内容:

1.(参数列表):括号中参数列表的数据类型,可以省略不写

2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略

3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)

注意:要省略{} return 分号必须一起省略

什么叫做推导?

就比如说jdk 1.7之前 创建集合对象必须把前后的泛型都写上

ArrayList temp=new ArrayList();

jdk1.7之后,=号后边的泛型可以省略.后边的泛型可以根据前边的泛型推导出来

例子

       Arrays.sort(persons,( o1,  o2)->
                o1.age-o2.age
        );//使用lamdba简化写法重写
//参照上面的例子



public class TestLamdba {
   
    public static void main(String[] args) {
   
      tempWrite(1, a->{
   //只有一个参数 省略() 类型
      System.out.println("11");
    });
     public static void tempWrite(int a,Temp temp){
   
        temp.write(a);
     }       
   }
    interface Temp{
   
    void write(int a);
    }      

lambda的使用前提

1.使用lamdba必须具有接口 且要求接口中有且仅有一个抽象方法.(不然它不知道你重写的哪个方法)

无论是jdk内置的Runnable Comparator接口还是自定义的接口 只有当接口中的抽象方法存在且唯一时才可以使用lambda

2.使用Lambda必须具有上下文推断

也就是方法的参数或局部变量类型必须为lamdba对应的接口类型 才能使用lamdba作为该接口的实例.

有且仅有一个抽象方法的接口 ,称为"函数式接口"

如果使用内部类的方式进行重写方法 那么就会多一个class 文件 但是如果使用lamdba进行的话以后加载内存的时候就会少一个class,使用lamdba的效率要比内部类好的多!!

使用lamdba进行延迟加载

有些场景的代码执行后,结果不一定要使用,从而造成性能浪费,而Lambda表示是延迟执行的,这正好可以作为解决方案,提升性能.

性能浪费的日志案例

public class Demo{
   
    private static void log(int level,String msg){
   
        if(level==1){
   
            System.out.println(msg);
        }
    }
    public static void main(String[] args){
   
        String msg1="Hello";
        String msg2="World";
        String msg3="java";
        log(2,msg1+msg2+msg3);
    }
}

以上代码的性能浪费

调用log方法,传递的第二个参数是一个拼接的后的字符串

先把字符串拼接好,然后在调用log方法,log方法中如果传递的日志等级不是1级那么就不会使用拼接后的字符串 所以感觉字符串就白拼接,存在了浪费.

使用lambda

interface MessageBuilder{
   
     String builderMessage();     
}
public class Demo{
   
    public static void log(int level,MessageBuilder mb){
   
        if(level==1){
   
            System.out.println(mb.builderMessage());
        }
    }
    public static void main(String[] args){
   
        String msg1="Hello";
        String msg2="World";
        String msg3="java";
        log(2,()->{
   
            return msg1+msg2+msg3;
        });
    }
}

使用lambda表达式作为参数传递,仅仅是把参数传递到log方法中

只有满足条件,日志的等级是1级

​ 才会调用接口MessageBuilder中的方法builderMessage

​ 才会进行字符串的拼接

如果条件不满足,日志的等级不是1级

​ 那么MessageBuilder中的方法builderMessage也不会执行

​ 所以拼接字符串的代码也不会执行

​ 所以不会浪费性能

1.7与1.9对流的异常处理

代码

public class Try7And9 {
   
    public static void main(String[] args)throws FileNotFoundException {
   
         // writeDataJdk7();
        writeDataJdk9();
    }
    /*JDK7处理IO流异常特性
     * 格式 try(
     *           定义流对象A;
     *           定义流对象B;
     *           ...........
     *            )catch(){
     *            处理异常
     *            }
     *  注:此方法不需要释放,出了try会自动释放*/
    public static void writeDataJdk7(){
   
        try(
                FileInputStream fis = new FileInputStream("C:\\Users\\asus\\Desktop\\小会同志.txt");
                FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\小会同1.txt");
        ){
   
            //     fis=null;此时的fis是常量不能修改
            int len = 0;
            while((len = fis.read())!=-1){
   
                fos.write(len);
            }
        }catch(IOException e){
   
            System.out.println(e);
        }
    }

    /*JDK 9处理IO异常新特性
     * 格式:
     *       定义对象A
     *       定义对象B
     *       try(A;B){
     *           内容
     *       }catch(){
     *       输出异常
     *       可以直接引用对象 同样也是自动关闭流
     *       }*/
    public static void writeDataJdk9()throws FileNotFoundException {
   
        FileInputStream fis = new FileInputStream("C:\\Users\\asus\\Desktop\\小会同志.txt");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\小会同志1.txt");
        try(fis;fos){
   
            //    fis=null;此时的fis是常量不能修改
            int len = 0;
            while((len = fis.read())!=-1){
   
                fos.write(len);
            }
        }catch(IOException e){
   
            System.out.println(e);
        }

    }

}

详情代码E:\后端修炼之路\jdk新特性\jdk\src\jdk\nine\Try7And9.java

Properties集合

java.util.Properties集合extends Hashtable<k,v>implements Map<k,v>
Properties类表示了一个持久的属性集。Properties可保存在流中或者从流中加载。
Properties集合是唯一和IO流相结合的集合。

可以使用Properties集合中的方法store,把集合中的零时数据,持久化写入到硬盘中存储。

可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用。
void load(InputStream inStream)
void load(Reader reader )
参数
InputStream inStream——字节输入流,不能读取含有中文的键值对。
Reader reader——————字符输入流,能读取含有中文的键值对。
使用步骤
1.创建Properties集合对象。
2.使用Properties集合对象中的方法load读取保存键值对的文件。
3.遍历Properties集合
注意
1.存储键值对的文件中,健与值默认的连接符号可以使用=,空格(其他符号)。
2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取。
3.存储键值对的文件中,健与值默认都是字符串,不用再加引号。
属性值列表中每个间及其对应值都是一个字符串。
Properties集合是一个双列集合,key和value默认都是字符串。
Properties集合中有一些操作字符串的特有方法。

Object setProperty(String key,String value)——调用Hashtable的方法put。

String getProperty(String key)———————通过key找到value值,此方法相当于Map集合中的get(key)方法。

setStringPropertyNames()—————放回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keyset方法。
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。

void store(OutputStream out,String comments)

void store(Writer writer,String comments)
参数
OutputStream out——字节输出流,不可以写中文
Writer writer————字符输出流,可以写中文
String comments——注释,用来解释说明保存的文件是做什么用的。
不能使用中文,会产生乱码,默认是Unicode编码
一般使用“空字符串”
使用步骤
1.创建Properties集合对象,添加数据。
2.创建字节输出流/字符输出流,构造方法中绑定要输出的目的地。
3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。
4.释放资源。

代码:

public static void main(String[] args) {
   
        Properties pr=new Properties();
        pr.put("name", "张三");
        pr.put("age", "18");
        
        try {
   
            FileWriter fw=new FileWriter(new File("src/text.properties"));
            pr.store(fw,"这是一段注释");
            
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }//写入文件
    
    
    
    public static void main(String[] args) {
   //读取文件
        //从指定文件读取
        try {
   
            Properties pr=new Properties();
            FileInputStream fis= new FileInputStream(new File("src/text.properties"));
            //使用"utf-8"编码
            InputStreamReader isr=new InputStreamReader(fis,"utf-8");
            pr.load(isr);
            String name=pr.getProperty("name");
            String age=pr.getProperty("age");
            System.out.println(name+"---"+age);
        } catch (Exception e) {
   
            // TODO Auto-generated catch block
            e.printStackTrace();
        }     
    }

1.8对接口方法的改变

首先可以声明static 与 default 的方法

default 它的出现使得接口 解决了 接口升级的问题 就接口中新添加了一个方法 实现类不用必须去重写它 也可以重写覆盖

public class DefultClass {
   
    public static void main(String[] args) {
   
          new Testson().text();//输出父类中的方法
    }
}
interface TestFather{
   
    
   default void text(){
   
      System.out.println("父类中的方法");
    }
   default void text1(){
   
       System.out.println("可以写多个default");
    }
    void write();
}

class Testson  implements TestFather{
   

    @Override
    public void write() {
   
        System.out.println("重写方法");
    }
}

但是下面的例子 如果2个父接口中相同的default方法那么就必须重写了

public class DefultClass {
   
    public static void main(String[] args) {
   
          new Testson().text();
    }
}
interface TestFather{
   
   default void text(){
   
      System.out.println("父类中的方法");
    }
    void write();
}
interface TestFather2{
   
    default void text(){
   
        System.out.println("父类中的方法2");
    }
}

class Testson  implements TestFather,TestFather2{
   

    @Override
    public void text() {
   //因为2个父接口中的默认方法相同  编译器不知道调用那个所以就必须要重写

    }

    @Override
    public void write() {
   
        System.out.println("重写方法");
    }
}

static关键字

public class StaticClass {
   
    public static void main(String[] args) {
   
         Test.write();//根据接口名调用  同样子类不能调用  因为静态方法只属于这个类
    }
}
interface Test{
   
    static void write(){
   
        System.out.println("我是接口的静态方法");
    }
}

1.9接口私有方法

目的就是为了解决接口中代码相同部分

public class PrivateClass {
   
    public static void main(String[] args) {
   
         Test2.Test2();
    }

}
interface Test{
   
    private void write(){
   
        System.out.println("方法中相同的代码");
    }
    default void Test1(){
   //default方法默认就是public
        write();
        System.out.println("实现功能1");
    }
    default void Test2(){
   
        write();
        System.out.println("实现功能2");
    }
}
interface Test2{
   
     private static void write(){
   
         System.out.println("方法中相同的代码");
     }
    static void Test1(){
   //static方法默认就是public
        write();
        System.out.println("实现功能1");
    }
    static void Test2(){
   
        write();
        System.out.println("实现功能2");
    }
}

1.8对函数接口的注解

首先什么是函数接口?

就是接口中只有一个抽象方法

函数接口跟普通接口其实没什么区别 只是实现的方式多了一个lambda方式

使用@FunctionalInterface注解限制

如果接口中没有一个抽象方法 编译错误 或者 有多个 编译错误 总的来说就一个接口只有一个抽象方法

@FunctionalInterface
interface myClass{
   
    void write();
//    void fun();
}

1.8中的常用函数接口

JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。

Supplier接口

下面是最简单的Supplier接口及使用示例。

// Supplier接口源码


@FunctionalInterface
public interface Supplier<T> {
   

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。如:

import java.util.function.Supplier;

public class Demo01Supplier {
   
    public static void main(String[] args) {
   
        String msgA = "Hello ";
        String msgB = "World ";
        System.out.println(
                getString(
                        () -> msgA + msgB
                )
        );
    }

    private static String getString(Supplier<String> stringSupplier) {
   
        return stringSupplier.get();
    }
}

练习求数组最大元素

使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。接口的泛型使用 java.lang.Integer 类。

import java.util.function.Supplier;

public class DemoNumberMax {
   
    public static void main(String[] args) {
   
        int[] numbers = {
   100, 200, 300, 400, 500, -600, -700, -800, -900, -1000};
        int numberMax = arrayMax(
                () -> {
   
                    int max = numbers[0];
                    for (int number : numbers) {
   
                        if (max < number) {
   
                            max = number;
                        }
                    }
                    return max;
                }
        );
        System.out.println("数组中的最大值为:" + numberMax);
    }

    /**
     * 获取一个泛型参数指定类型的对象数据
     * @param integerSupplier 方法的参数为Supplier,泛型使用Integer
     * @return 指定类型的对象数据
     */
    public static Integer arrayMax(Supplier<Integer> integerSupplier) {
   
        return integerSupplier.get();
    }

此接口指定什么泛型就返回什么类型

Consumer接口

源码

@FunctionalInterface
public interface Consumer<T> {
   

    /**
     * 对给定参数执行消费操作。
     *
     * @param t 输入参数
     */
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
   
        Objects.requireNonNull(after);
        return (T t) -> {
    accept(t); after.accept(t); };
    }
}

java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。

Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。基本使用如:

代码:

import java.util.function.Consumer;

public class MyFuntion {
   
    public static void main(String[] args) {
   
        write("tam"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值