JavaSE Day14 异常 泛型


Lambda表达式调用Arrays类中的方法

parallelSetAll()

package day14;

import java.util.Arrays;
import java.util.function.IntUnaryOperator;

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        // 一元运算,用运算的结果来替换数组中的所有元素
        Arrays.parallelSetAll(arr, new IntUnaryOperator() {
            @Override          // 数组的索引,
            public int applyAsInt(int operand) {
                return operand + 1;
            }
        });

        Arrays.parallelSetAll(arr, index -> {
            return index+1;
        });
        System.out.println(Arrays.toString(arr));
    
    }
}

parallelPrefix()

package day14;

import java.util.Arrays;
import java.util.function.IntBinaryOperator;

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        // 二元运算,用于运算的结果,来替换数组中的所有元素
        Arrays.parallelPrefix(arr, new IntBinaryOperator() {
            @Override            // left:前一个元素,right当前元素,当前元素是数组中第一个元素,前一个元素是1;
            public int applyAsInt(int left, int right) {
                return left * right;
            }
        });

        // 当前元素 和 替换后的元素进行计算,得出的结果在赋值给当前元素的位置;
        System.out.println(Arrays.toString(arr));

        Arrays.parallelPrefix(arr,((left, right) -> {
            return left* right;
        }));

        System.out.println(Arrays.toString(arr));
       
    }
}

Stream()

package day14;

import java.util.Arrays;
import java.util.function.IntConsumer;

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
         // Stream
        Arrays.stream(arr).forEach(new IntConsumer() {
            @Override         // value 数组中的每个元素
            public void accept(int value) {
                System.out.println(value);
            }
        });

        // Lambda
        Arrays.stream(arr).forEach(value -> System.out.println(value));
        
        // 方法引用
        Arrays.stream(arr).forEach(System.out::println);
       
    }
}

Lambda练习

Lambda1

package day14;


abstract class Animal{
    public abstract void breed();
}

@FunctionalInterface
interface Fly{
    void fly();
}

@FunctionalInterface
interface Talk{
    void talk();
}


class WileGoose extends Animal{
    @Override
    public void breed() {
        System.out.println("大雁能繁殖");
    }

    Fly fly = () -> System.out.println("大雁能飞");
    /*public void fly(Fly fly){
        fly.fly();
    }*/
}

class Duck extends Animal{


    @Override
    public void breed() {
        System.out.println("鸭子能繁殖");
    }


    public void swimming(){
        System.out.println("鸭子会游泳");
    }
}

class DonaldDuck extends Duck{
    // 直接在属性中设置Lambda,
    Talk talk = ()-> System.out.println("唐老鸭能说话");
    // 需要在主函数设置;不推荐;
    public void speak(Talk talk){
        talk.talk();
    }
}



public class TestLamabda3 {
    public static void main(String[] args) {
        DonaldDuck donaldDuck = new DonaldDuck();
        donaldDuck.talk.talk();

        WileGoose wileGoose = new WileGoose();
        wileGoose.fly.fly();

    }
}

异常处理

一般都是为了处理非正常的情况,改变程序的执行流程。

异常的分类

异常分类图

Error 异常

Error异常都是用代码处理不了的

Exception异常

用代码可以处理的异常;

Exception类的包含了运行时异常和非运行时异常;

运行时异常

RunTimeException类和它的子类;
编译可以通过,但是在运行期出现的问题;使程序中断了;

非运行时异常

Exception自身 和 它的部分子类(因为包含了RunTimeException)
编译时出现问题
是必须要处理的异常。

常见的异常

package day14;

import java.lang.reflect.Field;
import java.util.Scanner;
class A{
    private int x;

}
public class TestException1 {
    public static void main(String[] args) throws Exception  {
        //空指针  java.lang.NullPointerException
        String s = " ";
        System.out.println(s.equalsIgnoreCase("abc"));
        //     java.util.InputMismatchException
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();

        // 类型转换java.lang.ClassCastException
       /* Object obj = new String();
        String s1 = (String)obj;
        Integer n1 = (Integer)obj;*/


        // 数字格式化 java.lang.NumberFormatException
        String s2 = "12a";
        //int n3 = Integer.valueOf(s2);


        // 不能加载类 java.lang.ClassNotFoundException
        // ClassLoader.getSystemClassLoader().loadClass("day14.TestException");

        // 访问x:到字节码文件中
        Class c = A.class;
        Field f = c.getDeclaredField("x");
        f.setAccessible(true);//需要授权,否则报错
        Object o = c.newInstance();
        //java.lang.IllegalAccessException 参数异常
        f.set(o,22);
        System.out.println(f.get(o));
    }

}

异常的处理方式

try-catch

语法:

try{
    可能会出现异常的代码
} catch(异常类类型 对象名){
    处理异常
}

执行结果

  1. 没有异常;
    try执行,catch没有执行
  2. 异常类型匹配,
    try执行,catch也执行
  3. 异常类型不匹配:
    try执行,catch不执行;但是程序会中断;

多重catch

语法

try{
    可能会出现异常的代码
} catch(异常类类型 对象名){
    处理异常
} catch(异常类类型 对象名){
    处理异常
} catch(异常类类型 对象名){
    处理异常
} ……

catch块中异常类类型的顺序

  1. 有普通 到特殊,
  2. 由子类到父类;
  3. catch块中可以增加return;catch块中可以增加return;来结束当前方法;

try-catch-finally

try{
    可能会出现异常的代码
} catch(异常类类型 对象名){
    处理异常
} …… catch(异常类类型 对象名){
    处理异常
}finally{ //关闭流
    一定会执行
}

特殊情况

  1. catch块中有returnfinally块也会执行;
  2. catch块中System.exit(0),就退出JVM了,因此就不执行finally块了;

throws

  1. 声明异常,把异常抛给调用者;

  2. 方法头部声明了Excepion,那么就说明方法体中存在 非运行时异常,是必须要处理的;

  3. 方法头部声明了RunTimeException,那么就说明方法体中存在 运行时异常,不是必须处理的;

++问题:什么时候需要throws声明异常++
throws与try-catch是一个级别的;
throws是将异常抛出给调用者,try-cathc是将异常自己处理了;
也就是说当调用了一个声明了异常的方法时,要么使用try捕获异常,要么使用throws抛出异常

throw

  1. 抛异常,
  2. 自己抛异常
  • 如果抛出的异常是 运行时异常(RunTimeException),在方法头部不用throws声明Exception
package homework02;

/**
 * 自定义颜色异常,继承自RunTimeException
 */
class ColorException extends RuntimeException{
    private String emessage;
    ColorException(String emessage){
        super();
        this.emessage = emessage;
    }

    @Override
    public String toString() {
        return this.emessage;
    }
}

/**
 * 猫类
 */
class Cat{
    /**
     * 颜色
     */
    private String color;

    public String getColor() {
        return color;
    }

    /**
     * 如果颜色不是红,黄,蓝,就抛出异常
     * @param color
     */
    public void setColor(String color) {
        if("红".equals(color) || "黄".equals(color) || "蓝".equals(color)) {
            this.color = color;
        }else{
            throw new ColorException("颜色只能是红、黄、蓝");
        }
    }
}
public class TestException {
    public static void main(String[] args) {
        Cat cat = new Cat();

        cat.setColor("红");
        cat.setColor("黑");
    }
}

  • 如果抛出的异常是 非运行时异常,那么就需要使用throws在方法头部声明Exception;

    package homework02;
    
    /**
     * 自定义颜色异常,继承自Exception
     */
    class ColorException extends Exception{
        private String emessage;
        ColorException(String emessage){
            super();
            this.emessage = emessage;
        }
    
        @Override
        public String toString() {
            return this.emessage;
        }
    }
    
    /**
     * 猫类
     */
    class Cat{
        /**
         * 颜色
         */
        private String color;
    
        public String getColor() {
            return color;
        }
    
        /**
         * 如果颜色不是红,黄,蓝,就抛出异常
         * @param color
         */
        public void setColor(String color) throws Exception {
            if("红".equals(color) || "黄".equals(color) || "蓝".equals(color)) {
                this.color = color;
            }else{
                throw new ColorException("颜色只能是红、黄、蓝");
            }
        }
    }
    public class TestException {
        public static void main(String[] args) throws Exception{
            Cat cat = new Cat();
    
            cat.setColor("红");
            cat.setColor("黑");
        }
    }
    
    
  1. 作用:手动抛出异常;

自定义异常

自定义异常,一般都是继承自两个类,分别是RunTimeException(运行时异常)和 Exception(非运行时异常);

当自定义异常继承自 Excetpion时,手动抛出异常的方法的头部必须 声明异常(throws Exception),

  • 并且如果在调用时使用try-catch捕获异常,那么最后必须有一个catch (Exception e)的catch语句;
  • 如果不捕获异常,那么需要在主方法的头部,声明异常(throws Exception);

当自定义异常继承自 RunTimeExcetpion时,手动抛出异常的方法的头部不需要 声明异常(throws),

  • 在使用try-catch捕获异常时,也不需要catch (Exception e)语句。
  • 如果不捕获异常,那么需要在主方法的头部,也不需要声明异常;

自定义 运行时异常

package homework01;


import java.util.Scanner;

/**
 * 自定义分数异常类,继承自RunTimeException,
 */
class ScoreException extends RuntimeException{
    /**
     * 分数错误时的信息
     */
    private String smessing;
    
    public ScoreException(String smessing) {
        super();
        this.smessing = smessing;
    }

    @Override
    public String getMessage() {
        return this.smessing;
    }
}

/**
 * 学生类
 */
class Student {
    private int Score;

    public int getScore() {
        return Score;
    }

    /**
     * 当分数不在 0~100之间时,抛出分数异常,
     * @param score 分数
     */
    public void setScore(int score) {
        if (score >=0  && score <=100) {
            Score = score;
        }else{
            throw new ScoreException("分数必须在0-100之间");
        }
    }
}

public class TestStudent{
    public static void main(String[] args) {
        Student s1 = new Student();
        Scanner input = new Scanner(System.in);

        /**
         * 当分数不在1~100范围时,需要重新输入;
         */
        while(true) {
            try {
                System.out.println("请输入成绩:");
                s1.setScore(input.nextInt());
                break;
            } catch (ScoreException e) {
                System.out.println(e.getMessage());
                // 由于Scanner里面有缓存,无法将错误的值赋值给变量。因此需要清空缓存,重新创建一个Scanner类;
                input = new Scanner(System.in);
            } 
        }
        System.out.println("设置成功。。。");
    }
}

自定义 非运行时异常

package homework01;


import java.util.Scanner;

/**
 * 自定义分数异常类,继承自Exception,
 */
class ScoreException extends Exception{
    /**
     * 分数错误时的信息
     */
    private String smessing;

    public ScoreException(String smessing) {
        super();
        this.smessing = smessing;
    }

    @Override
    public String getMessage() {
        return this.smessing;
    }
}

/**
 * 学生类
 */
class Student {
    private int Score;

    public int getScore() {
        return Score;
    }

    /**
     * 当分数不在 0~100之间时,抛出分数异常,
     * @param score 分数
     */
    public void setScore(int score) throws Exception{
        if (score >=0  && score <=100) {
            Score = score;
        }else{
            throw new ScoreException("分数必须在0-100之间");
        }
    }
}

public class TestStudent{
    public static void main(String[] args) {
        Student s1 = new Student();
        Scanner input = new Scanner(System.in);

        /**
         * 当分数不在1~100范围时,需要重新输入;
         */
        while(true) {
            try {
                System.out.println("请输入成绩:");
                s1.setScore(input.nextInt());
                break;
            } catch (ScoreException e) {
                System.out.println(e.getMessage());

            } catch (Exception e){
                System.out.println("未知错误");
            }
        }
        System.out.println("设置成功。。。");
    }
}

总结

throw和throws的区别

  • throw
    在代码块中执行,主要是手工进行异常类的抛出
  • throws
    在方法定义上使用,表示此方法可能产生的异常明确告诉给调用处,有调用处进行处理;

关于循环中Scanner重复输入的问题

Scanner 类的input方法在try语句中时,如果需要在catch语句中重新赋值,那么就需要在catch中重新创建一个Scanner的对象,否则Scanner中有缓存机制,可能会出现跳过赋值,直接抛出异常的情况;

异常处理原则

  1. 只用于处理非正常的情况
  2. 避免过大的try块
  3. 推荐使用多重catch
  4. 不要忽略catch块中的异常
  5. 改正代码
  6. 文档声明 - 声明了异常的方法要详细的说明异常;

泛型

回顾方法重写

  • 子类中,实例方法,方法名相同,参数列表相同,返回值类型形同
    1. 访问修饰符权限比父类要大
    2. 抛出的异常范围要比父类小
    3. 返回值类型可以是父类返回值类型的子类;
    4. 父类参数擦除后与子类一致;

概念

参数化类型

类型参数

  1. 定义时:形式类型参数
  2. 应用时:实际类型参数(必须是引用类型)

参数化类型

类型<实际类型参数>
合在一起就叫参数化类型;

好处

  1. 在编译器进行类型检查;
  2. 类型不确定。

定义泛型类

语法

Class  类名<类型参数1,类型参数2>{

}

参数类型

用一个大写字母表示;

  1. T -> Type
  2. K -> Key
  3. V -> Value
  4. E -> Element

多参数类型

可以定义多个类型参数,用逗号分隔;

原生类型

一个类型的后边没有指定具体的类型参数,这样的泛型类型,称为原生类型

字节码文件对泛型的处理

字节码文件中 擦除泛型类型信息

通配符

  1. ? 无界通配符 匹配任意类型
  2. ? extends 上限类 匹配上限类和上限的子类
  3. ? Super 下限类 匹配下限类和下限类的父类

提问

String类 是 Object类 的子类,Object 对象 = String 对象,自动转型,那么Point和Point有什么关系?
Point<String>不是Point<Object>的子类型;
通过查看字节码文件发现 Point的class文件就一个,因此不存在继承关系;

类型参数与通配符的区别

  1. 类型参数可以表示一种类型,泛型类型;通配符不能表示为一种类型。
  2. 类型参数只能指定上限;通配符能指定上限,和下限;
  3. 类型参数 可以指定多个上限;(能匹配各个上限的任一子类,); 通配符不能指定多个上限;

泛型构造器

类名<对象泛型> 对象名 = new <构造器泛型>类名<对象泛型>();

package day14;

class Point2<T> {
    private T x;
    private T y;

    // 泛型构造器的定义
    public <E> Point2(E e) {
        System.out.println(e);
    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}

public class TestPoint3 {
    public static void main(String[] args) {
        // 自动类型推断: 用参数的类型 自动推断出构造器的类型
        Point2<String> p = new Point2<>(11);

        //构造器的类型如果是显示指定,那么对象的类型也要用显示指定的 格式 new <构造器泛型>类名<对象泛型>();
        Point2<String> p1 = new <Integer>Point2<String>(11);


    }
}

泛型方法

package day14;

class Demo{
    // 泛型方法
    public <T> void f(T t){
        System.out.println(t);
    }
    public <T> T ff(T t){
        // 类型推断
        f(22);// 调用方法
        // 显示指定,必须使用对象调用
        this.<Double>f(22.2);
        return t;
    }
}
public class TestPoint4 {
    public static void main(String[] args) {
        Demo demo = new Demo();
        // 类型推断
        demo.f("hello");
        demo.f(123);

        //显示指定方法类型
        demo.<Double>f(22.2);

    }
}

擦除的原则

对于参数化类型,

擦除之后为 原生类型

Point3<String> p; -> point3 p

对于类型参数

无界类型参数,擦除后为Object;

Class Point<T>{

  T x;

} -> class Point<Object>{

  Object x;

}

有一个上限的类型参数,用上限类型替换;

<T extends A> -> A

有多个上限的类型参数,用第一个上限来替换;

<T extends A && B> --> A

泛型重载和重写

package day14;
// 泛型类
class Point3<T>{
    private T x;
    private T y;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}
class Demo2{}
interface IX{}
interface IY{}
class Demo1{
    // public void f(Object t){}
    public <T> void f(T t){}
    // 是否能重载:擦除后就一样了;
//    public void f(Point3<String> p){ }
    public void f(Point3<Integer> p){}

    //                            Demo2
    public <T extends Demo2> void f(T t){}
    //                               IX
    public <T extends IX & IY> void f(T t){}
    //                               IY
//    public <T extends IY & IX> void f(T t){}
}
class Parent1{
    //          f(Point3 p)
    public void f(Point3<String> p){}

}
class Child2 extends Parent1{
    @Override
    public void f(Point3 p) {
    }
}
public class TestPoint5 {
    public static void main(String[] args) {

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章