Java基础-6(可变参数,Collections工具类,Map集合,异常)

33.可变参数

在IDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格
式∶

修饰符返回值类型方法名(参数类型...形参名){}

其实这个书写完全等价与

修饰符返回值类型方法名(参数类型[]形参名){}

只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。
JDK1.5以后。出现了简化操作。 … 用在参数上,称之为可变参数。

同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。

/*
    可变参数:是DK1.5之后出现的新特性
    使用前提:
    	当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数.
    使用格式:定义方法时使用
    	修饰符返回值类型方法名(数据类型...变量名){}
    可变参数的原理:
        可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数
        传递的参数个数,可以是0个(不传递),1,2...多个
*/

public class Demo01VarArgs {
    public static void main(String[]args) {
    	//int i=add();
        //int i=add(10);
        //int i=add(10,20)
        int i = add(10,20,30,40,50,60,70,80,90,100);
		System.out.println(i);
        
        //method("abc",5.5,10,1,23,45)
    }
    /*
    可变参数的注意事项
        1.一个方法的参数列表,只能有一个可变参数
        2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
	*/
    /*public static void method(int. . .a ,String. ..b){
	}*/
    /*
    //int...a如果放前面会报错
    public static void method(string b,double c,int d,int...a){
    }
	*/
    //可变参数的特殊(终极)写法
	public static void method(Object. ..obj){
    }

    
    /*
    	定义计算(0-n)整数和的方法
        已知:计算整数的和,数据类型已经确定int
        但是参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数
        add();就会创建一个长度为8数组,new int[0]
        add(10);就会创建一个长度为1的数组,存储传速来过的参数new int[]{10};
        add(10,20);就会创建一个长度为2的数组,存储传递来过的参数new int[]{10,20};
    */
    
    public static int add(int...arr){
       	//System.out.println(arr);//[I@2ac1fdc4底层是一个数组
		//System.out.println(arr.length);//0
        
        //定义一个初始化变量,存储累加求和
        int sum = e;
		//遍历数组,获取数组中的每一个元素
		for (int i : arr) {
            //累加求和
            sum += i;
        }
        //把求和结果返回
        return sum;
    }
    /*
    //定义一个方法,计算两个int类型整数的和
    public static int add(int a,int b){
    	return a+b;
    }
	//定义三个方法,计算两个int类型整数的和
    public static int add(int a,int b,int c){
    	return a+b+c;
    }
    */ 
}
34.Collections工具类

java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:
public static <T> boolean addAll(Collection<T> c,T... elements)):往集合中添加一些元素。
public static void shuffle(List<?> list)打乱顺序:打乱集合顺序。
public static <T> void sort(List<T list):将集合中元素按照默认规则排序。
public static <T> void sort(List<T〉 list ,Comparator<? super T>):将集合中元素按照指定规则排序。

/*
    java.utils.collections是集合工具类,用来对集合进行操作。部分方法如下:
    -public static <T> boolean addALL(Collection<T〉c,T... eLements):往集合中添加一些元素。
    - public static void shuffle(List<?> list)打乱顺序:打乱集合顺序。
*/
public class Demoe1collections {
    public static void main(String[]args) {
        ArrayList<String> list = new ArrayList<>();
        //往集合中添加多个元素
    	/*
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        */
        //public static <T> boolean addALL(Collection<T〉 c,T... elements):往集合中添加一些元素。
		collections.addALL(list ,"a" ,"b","c", "d" , "e");
        System.out.println(list);//[a,b,c,d,e]
        
        // public static void shuffLe(list<?> list)打乱顺序:打乱集合顺序。
        collections.shuffLe(list);
        System.out.println(list);

    }
}

第一个sort方法

public class Person impiements Comparable<Person>{
    private string nam;
    private int age;
    /*默认已写了无参和有参构造方法,成员变量对应的set和get方法*/
    //重写排序的规则
    @override
    public int compareTo(Person o){
        //return 0;//认为元素都是相同的
       //自定义比较的规则,比较两个人的年龄(this,参数Person)
		//return this.getAge() - o.getAge();//年龄升序排序
        return o.getAge() - this.getAge();//降序
    }
}
/*
    java.utils.collections是集合工具类,用来对集合进行操作。部分方法如下:
    	public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
    注意:
    sort(list<T> list)使用前提
    被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则
    
    comparable接口的排序规则:
		自己(this)-参数:升序


*/
public class Demo02sort {
    public static void main(String[]args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println( list01);//[1,3,2]
        //public static <> void sort(List<T> list):将集合中元素按照默认规则排序。
        Collections.sort(list01);/╱黑认是升序
        System.out.print1n(list01);//[1,2,3]
        ArrayList<String> list02 = new ArrayList<>();
        list02.add("a");
        list02.add("c");
        list02.add("b");
        System.out.println(liste2);//[a, c, b]
        
        Collections.sort(list02);
        System.out.println(list02);

    }
}

第二个sort方法

public class Student{
    private String name;
    private int age;
    /*默认已写无参和满参构造函数,对应的set和get方法以及重写了toString方法*/
}

/*
    - java.utils.collections是集合工具类,用来对集合进行操作。部分方法如下:
    public static <T> void sort(List<T>list,Comparator<?super T>):将集合中元素按照指定规则排序。
    Comparator和Comparable的区别
    Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
    comparator:相当于找一个第三方的裁判,比较两个
    
    
*/
public class Demo03sort{
    public static void main(String[]args) {
        ArrayList<Integer> list01 = new Arraylist<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01);//[1,3,2]
        Collections.sort(liste1,new Comparator<Integer>() {
            //重写比较的规则
            @override
            public int compare( Integer o1, Integer o2) {
                //return o1-O2;//升序
                return o2-o1;//降序
            }
        });
        System.out.println(list01);
        
        ArrayList<Student> list02 = new ArrayList<>()﹔
        list02.add(new Student("迪丽热巴",18));
        list02.add(new Student("古力娜扎", 20));
        list02.add(new Student("杨幂",17));
        /*
		Collections.sort(liste2,new Comparator<Student>(){
            @Override
            public.int compare(Student o1,Student o2) {
                //按照年龄升序排序
                return o1.getAge()-o2.getAge();
                }
            });
            */
        Collections.sort(liste2,new Comparator<Student>(){
            @Override
            public int compare(Student o1,Student o2) {
                //按照年龄升序排序
                int o1.getAge()-o2.getAge();
                //如果两个人年龄相同,再使用姓名的第一个字比较
                if(result==0){
                	result = o1.getName().charAt(0)-o2.getName( ).charAt(0);
                return result;
                }
            }
            });
            
        System.out.println(list02);
    }
}
35.Map集合

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口。

在这里插入图片描述

  1. Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
  2. Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的
    值。
  3. Collection中的集合称为单列集合,Map中的集合称为双列集合。
  4. 需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

特点:

1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
3.Nap集合中的元素,key是不允许重复的,value是可以重复的
4.Map集含中的元素,key和value是——对应

  • Map的子类
  1. HashMap<K,V> ∶存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
  2. LinkedHashMap<K,V> :HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

tips : Map接口中的集合都有两个泛型变量,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。

java.utiL.HoshMap<k,v>集合implements Map<k,v>接口

HashMap集合的特点:
1.HashMap集合底层是哈希表:查询的速度特别的快
	JDK1.8之前:数组+单向链表
	JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8):提高查询的速度

2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致

java.util.linkedHashMap<k,v>集合extends HashMap<k,v>集合

LinkedHashMap的特点:
	1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
	2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
  • Map集合常用方法:
  1. public V put(K key,V value):把指定的键与指定的值添加到Map集合中。
  2. public V remove(Object key):把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。
  3. public V get(Object key)根据指定的键,在Map集合中获取对应的值。
  4. boolean containsKey(Object key)判断集合中是否包含指定的键。
  5. public Set<K> keySet():获取Map集合中所有的键,存储到Set集合中。
  6. public Set<Map.Entry<K,V>> entrySet();获取到Map集合中所有的键值对对象的集合(Set集合)。
public class Demo01Map{
    public static void main(String[]args){
        //show01();
        //show02();
        //show03();
    }
    /*
    public V put(K key,v value):把指定的键与	指定的值添加到Map集合中。
    返回值:v
        存储键值对的时候, key不重复,返回值v是null
        存储键值对的时候,key重复,会使用新的vaLue替换map中重复的value,返回被替换的vaLue值
    */
    private static void show01(){
        //创建Map集合对象,多态
        Map<string,string> map = new HashMap<>();
        String v1 = map.put("李晨""范冰冰1");
        System.out.println("v1: "+v1);//v1:null
        String v2 = map.put("李晨”,“范冰冰2");
        System.out.println("v2 : "+v2);//v2:范冰冰2
        
        System.out.println(map);//{李晨=范冰冰2}
		map.put("冷锋","龙小云")﹔
        map.put("杨过","小龙女")﹔
        map.put("尹志平","小龙女")﹔
        System.out.println(map);//{杨过=小龙女,尹志平=小龙女,李晨=范冰冰2,冷锋=龙小云}
    }
    /*
    public V remove(object key):把指定的键所对应的键值对元素在MNap集合中删除,返回被删除元素的值。
        返回值:V
        key存在, v返回被删除的值
        key不存在, v返回null
    */
    private static void show02(){
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        System.out.println(map);//{林志玲=178,赵丽颖=168,杨颖=165}

        Integer v1 = map.remove("林志玲")﹔
		System.out.println("v1: "+v1);//v1:178
        System.out.println(map);//{赵丽颖=168,杨颖=165}
		
        //int v2 = map.remove( "林志颖");//自动拆箱NuLLPointerException
        Integer v2 = map.remove( "林志颖");
        System.out.println("v2: "+v2);//v2:null
        System.out.println(map);//{赵丽颖=168,杨颖=165}
    }
	/*
        public v get(object key)根据指定的键,在Nap集合中获取对应的值。
        返回值:
        key存在,返回对应的value值
        key不存在,返回null
    */
    private static void show03() {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        Integer v1 = map.get(“杨颖");
        System.out.println("v1: "+v1);//v1:165
        Integer v2 = map.get("迪丽热巴");
        System.out.println("v2:"+v2);//v2:null

    }

	/*
        boolean containsKey(object key)判断集合中是否包含指定的键。
        包含返回true.不包含返回false
	*/
	private static void show04() {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        boolean b1 = map.containsKey("赵丽颖");
        System.out.println("b1:"+b1);//b1:true
        boolean b1 = map.containsKey("赵丽颖");
        System.out.println("b1: "+b1);//b2:false
    } 
}

Map集合遍历找值方式:

键找值方式︰即通过元素中的键,获取键所对应的值
分析步骤∶
1.获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示: keyset()
2.遍历键的Set集合,得到每一个键。
3.根据键,获取键所对应的值。方法提示: get(K key)

在这里插入图片描述

/*
    Map集合的第—种遍历方式:通过键找值的方式
    Map集合中的方法:
    set<k> keyset()返回此映射中包含的键的set视图。
    实现步骤:
    1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个set集合中
    2.遍历set集合,获取Map集合中的每一个key
    3.通过Map集合中的方法get( key),通过key找到value
*/
public class Demo02Keyset {
    public static void main(String[]args) {
    	//创建Map集合对象
        Map<String, Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        
        //1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个set集合中
        Set<String> set = map.keySet();
        //2.遍历set集合,获取Map集合中的每一个key
        //使用迭代器偏历set集合
		Iterator<String> it = set.iterator();
        while (it.hasNext()){
        	String key = it.next();
            //3.通过Map集合中的方法get( key),通过key找到value
            Integer value = map.get(key) ;
            System.out.println(key+"="+value);
        }
        System.out.println("----------");
        //使用增强for遍历set集合
        for(String key : set){
            //3.通过Map集合中的方法get ( key),通过key找到value
            Integer value = map.get( key);
            System.out.println(key+"="+value);
        }
         System.out.println("----------");
        
        //使用增强for遍历set集合,节省了一行代码Set<String> set = map.keySet();
        for(String key : map.keySet()){
            //3.通过Map集合中的方法get ( key),通过key找到value
            Integer value = map.get( key);
            System.out.println(key+"="+value);
        }       
    }
}
  • Entry键值找对象

我们已经知道,Map 中存放的是两种对象,一种称为key(键),一种称为value(值),它们在Map 中是一一对应关系,这一对对象又称做map中的一个Entry(项)Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry )对象中获取对应的键与对应的值。
既然Entry表示了一对键和值,那么也同样提供了获取对应键和对应值得方法∶
public K getKey():获取Entry对象中的键。
public v getValue()∶获取Entry对象中的值。
在Map集合中也提供了获取所有Entry对象的方法:
public Set<Map.Entry<K,v>> entryset():获取到Map集合中所有的键值对对象的集合(Set集合)。

在这里插入图片描述

/*
    Map集合遍历的第二种方式:使用Entry对象遍历
    Map集合中的方法:
    	Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射关系的set视图。
    实现步骤:
        1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个set集合中
        2.遍历set集含,获取每一个Entry对象
        3.使用Entry对象中的方法getKey(和getvalue()获取键与值
*/
public class Demo03Entryset{ 
    public static void main(String[] args) {
        //创建Map集合对象
        Map<String, Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个set集合中
        Set<Map.Entry<String,Integer>> set = map.entrySet();
        
        //2.遍历set集合,获取每一个Entry对象
        //使用迭代器遍历set集合
        Iterator<Map.Entry<String,Integer>> it = set.iterator();
        while(it.hasNext()){
            Map.Entry<String,Integer> entry = it.next();
            //3.使用Entry对象中的方法getKey()和getvalue()获取键与值
            String key = entry.getKey();
            Integer value = entry.getValue( );
            System.out.println(key+"="+value);
        }
		system.out.println("--------------");
        for(Map.Entry<String,Integer> entry:set){
            //3.使用Entry对象中的方法getKey()和getvalue()获取键与值
            String key = entry.getKey();
            Integer value = entry.getValue( );
            System.out.println(key+"="+value);
        }
    }
}
  • HashMap存储自定义类型键值
public class Person{
    private String name;
    private int age;
    /*默认已写无参和满参构造函数,对应的set和get方法*/
    //设已重写toString,hashCode,equals方法
    @override
    public string tostring()i
        return "Person{"+
        "name='"+name +'\''+
        ",age=" + age +
        '}';
    }
    @override
    public boolean equals(0bjelt o)i
        if (this == o) return true;
        if (o == null || getclass() != o.getclass()) return false;
        Person person = (Person) o;
        return age == person.age &&
        Objects.equals(name,person.name);
    }

}
    /*
    HashMap存储自定义类型键值
    Aap集合保证key是唯一的:
    作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
    */
public class Demo01HashMapSavePerson {
    public static void main(String[]args) {
    	//show01();
    }
    /*
        HashMap存储自定义类型键值
        key : String类型
        	String类重写hashcode方法和equals方法,可以保证key唯一
      	value:Person类型
      		value可以重复(同名同龄视为同一个人)
    */
    private static void show01() {
		//创建HashMap集合
        HashMap<String,Person> map = new HashMap<>();
        //往集合中添加元素
        map.put("北京",new Person( "张三",18));
        map.put("上海" ,new Person( "李四",19));
        map.put("广州",new Person("王五", 20));
        map.put("北京",new Person( "赵六",18));
		//使用keyset加增强for遍历Map集合
        Set<String> set = map.keySet();
        for (String key : set) {
            Person value = map.get( key );
            System.out.println( key+"-->"+value);
		}
    }
    /*
    	HashMap存储自定义类型键值
        key : Person类型
        person类就必须重写hashCode方法和equals方法,以保证key唯一
        value:String类型
        可以重复
	*/
	private static void show02() {
        //创建HashMap集合
        HashMap<Person,String> map = new HashMap<>();
        //往集合中添加元素
        map.put(new Person( "女王",18),"英国");
        map.put(new Person( "秦始皇", 18),"秦国");
        map.put(new Person( "普京", 30),"俄罗斯");
        map.put(new Person( "女王",18),"毛里求斯");
		//使用entryset和增强for遍历Map集合
        Set<Map.Entry<Person,String>> set = map.entrySet();
        for (Map.Entry<person,String> entry : set) {
            Person key = entry.getKey();
            String value = entry.getvalue();
            System.out.println( key+"-->"+value);
        }
    }
}

  • LinkHashMap
/*
java.util.LinkedHashMap<K,v>entends HashMap<K,V>
Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。
底层原理:
哈希表+链表(记录元素的顺序)
*/
public class Demo01LinkedHashMap {
    public static void main(String[]args) {
        HashMap<String,String> map = new HashMap<>();
        map.put("a","a" );
        map.put("c","c");
        map.put("b","b");
        map.put("a", "d");
        System.out.println(map);// key不允许重复,无序{a=d,b=b,c=c}

	 	LinkedHashMap<String,String> linked = new LinkedHashMap<>();
        linked.put("a","a" );
        linked.put("c","c");
        linked.put("b","b");
        linked.put("a", "d");
        System.out.println(linked);// key不允许重复,有序,{a=d,c=c,b=b}
    }
}
  • HashTable集合

因为引用类型默认值为null,因此其他集合可以使用null对象,但HashTable不同,不能存储null,任何非 null对象都可以用作键或值;是同步的,单线程的;

java.util.Hashtable<K,V>集合impLements Map<K,V>接口

Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快

HashMap集合(之前学的所有的集合):可以存储null值, nulL键
HashtabLe集合,不能存储null值, nulL键

Hashtable和vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
HashtabLe的子类Properties依然活跃在历史舞台
Properties集合是一个唯—和Io?流相结合的集合

public class Demo02Hashtable {
    public static void main(String[]args) {
        HashMap<String,String>map = new HashMap<>();
        map.put(null,"a" );
        map.put("b",null);
        map.put(null,null);
		System.out.println(map);//{null=null, b=null}
        
        Hashtable<String,String> table = new Hashtable<>();
       	//tabLe.put(null,"a");//NuLLpointerException
        //table.put("b",null);//NuLLpointerException
        table.put(null,null);//NuLLpointerException
    }
}
  • JDK9对集合添加的优化

JDK9的新特性:
List接口,set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
static <E> List<E> of (E... elements)

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

注意:
1.of方法只适用于List接口,set接口,Map接口,不适用于接口的实现类(即HashMap,ArrayList等)。

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

3.Set接口和wap接口在调用of方法的时候,不能有重复的元素,否则会抛出异常。

public class Demo01JDK9{
    public static void main(String[]args) {
		List<String> list = list.of("a","b","a","c","d");
    	System.out.println(list);
        //list.add("w"); //UnsupportedoperationException:不支持操作异常
        
  //Set<String> set = Set.of("a","b","a","c","d");//IllegaLArgumentException:非法参数异常,有重复的元素
        Set<String>set = Set.of("a","b","c","d");
        System.out.println(set);
		//set.add("w");//UnsupportedoperationException:不支持操作异常
        
        //Map<String,Integer> map = Map.of("张三",18,"李四",19,"王五",20,"王五",20);//IllegaLArgumentException:非法参数异常,有重复的元素
        Map<String,Integer> map = Map.of("张三",18,"李四",19,"王五",20);
		System.out.println(map);
		map.put("赵四",20);//UnsupportedoperationException:不支持操作异常
	}
}
36.IDEA的debug调试

Debug调试程序:
可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug

使用方式:

在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
右键,选择Debug执行程序
程序就会停留在添加的第一个断点处

执行程序:

f8:逐行执行程序
f7:进入到方法中
shift+f8:跳出方法
f9:跳到下一个断点,如果没有下一个断点,那么就结束程序
ctrL+f2:退出debug模式,停止程序
console:切换到控制台

37.异常
  • 异常︰指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。

在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。

异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.

  • 体系

异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:
java.lang.Errorjava.lang.Exception,平常所说的异常指java.lang.Exception

在这里插入图片描述

Throwable体系:

  1. Error : 严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
  2. Exception :表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒,阑尾炎

常用方法

  1. public void printStackTrace():打印异常的详细信息。包含了异常的类型异常的原因还包括异常出现的位置,在开发和调试阶段都得使用printStackTrace。
  2. public String getMessage():获取发生异常的原因。提示给用户的时候就提示错误原因。
  3. public String tostring():获取异常的类型和异常描述信息(不用)。

出现异常,可以把异常的简单类名,拷贝到API中去查。

  • 异常分类

异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?

  1. 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化常)
  2. 运行时期异常:runtirme异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)

在这里插入图片描述

/*
java.Lang. Throwable:类是Java语言中所有错误或异常的超类。
    Exception:编译期异常,进行编译(写代码)java程序出现的问题
    RuntimeException:运行期异常,java程序运行过程中出现的问题
异常就相当于程序得了一个小毛病(感冒,发烧),把异常处理掉,程序可以继续执行(吃点药,继续革命工作)
Error:错误
    错误就相当于程序得了一个无法治愈的毛病(非典,艾滋).必须修改源代码,程序才能继续执行
*/
public class Demo01Exception {
    public static void main(String[]args) /*throws ParseException*/{
        //Exception:编译期异常,进行编译(写代码)java程序出现的问题
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd");//用来格式化日期
        /*
        如果通过throws ParseException即抛出异常,会将异常交给虚拟机处理,
        当代码没有问题时,程序不会报错
        当代码出现问题时,代码会中止,并将异常打印出来
        */
       // Date date = sdf.parse("1999-09-09");//正常情况,把字符串格式的日期,解析为Date格式的日期
 		//Date date = sdf.parse("1999-0909");//异常情况
        /*
        Date date = null;
        try {
        date = sdf.parse( source: "1999-0909");//把字符串格式的日期,解析为Date格式的日期
        }
        catch (ParseException e) {
        	e.printstackTrace( );
        }
        System.out.println(date);
        System.out.println("后续代码");
        */
        
        /*
       		runtimeException()
		*/
        error();
	}
    
    public void runtimeException(){
         //RuntimeException:运行期异常,java程序运行过程中出现的问题
        int[]arr = {1,2,3};
        //system.out.println(arr[o]);
        try {
            //可能出现异常的代码
        	system.out.println(arr[3]);
        }catch(Exception e){
            //异常的处理逻辑
       		 System.out.println(e);
        }
		System.out.println("后续代码");
    }     
    
    public void error(){
        /*
        Error:错误
        outofMemoryError: Java heap space
        内存流出的错误,创建的数组太大了,超出了给JV分配的内存
        */
        //int[]arr = new int[1024*1024*1024];
        //必须修改代码, 即创建的数组小一点;
        int[]arr = new int[1024*1024];
        System.out.println("后续代码“);
    }
}
  • 异常的产生解析
/*
异常的产生过程解析(分析异常的怎么产生的,如何处理异常)
*/
public class Demo02Exception {
    public static void main(String[]args) {
        //创建int类型的数组,并赋值
        int[]arr = {1,2,3};
        int e = getELement(arr,3);
        System.out.println(e);
    }
    /*
    定义一个方法,获取数组指定索引处的元素
    参数:
        int[] arr
        int index
    */
	public static int getElement(int[] arr,int index){
		int ele = arr[index];
		return ele;
	}
}

在这里插入图片描述

  • 异常的处理

Java异常处理的五个关键壹:try,catch,finally,throw,throws

在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操作呢?

1.创建一个异常对象。封装一些提示信息(信息可以自己编写)。

2需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw异常对象。

throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。

使用格式:

throw new 异常类名(参数);
/*
    throw关键字
    作用:
    	可以使用throw关键字在指定的方法中抛出指定的异常
    使用格式:
    	throlw new xxxException("异常产生的原因")﹔
    注意:
        1.throw关键字必须写在方法的内部
        2.throw关键字后边new的对象必须是Exception或者Exception的子类对象
        3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
			throw关键字后边创建的是RuntimeException或者是RuntimeException的子类对象,我们可以不处理默认交给JvW处理(打印异常对象,中断程序)
			throw关键字后边创建的是编译异常(写代码的时候报错))我们就必须处理这个异常,要么throws,要么try...catch
*/

public class Demo03Throw{
    public static void main(String[]args) {
        //int[]arr = null;
        int[]arr = new int[3];
        int e = getELement(arr, 3);
        System.out.println(e);
    }
    /*
        定义一个方法,获取数组指定索引处的元素
        参数:
        int[]arr
        intindex
        以后(工作中)我们首先必须对方法传递过来的参数进行合法性校验
        如果参数不合法,那么我们就必须使用抛出异常的方式,告知污法的调用者,传递的参数有问题
        注意:
        	NullPointerException是一个运行期异常,我们不用处理,默认交给JV处理
        	ArrayIndexoutofBoundsException是一个运行期异常,我们不用融理,默认交给JVM处理
    */
    public static int getElement(int[]arr,int index){
        /*
            我们可以对传递过来的参数数组,进行合法性校验
            如果数组arr的值是null
            那么我们就抛出空指针异常,告知方法的调用者"传递的数组的值是null"
        */
        if(arr == null){
        	throw new NullpointerException("传递的数组的值是null")
        }
        
        /*
        	我们可以对传递过来的参数index进行合法性校验
            如果index的范围不在数组的索引范围内
            那么我们就抛出数组索引越界异常,告知方法的调用者"传递的索引超出了数组的使用范围”
        */
        if(index<0||index>arr.length-1){
        	throw new ArrayIndexoutofBoundsException("传递的索引超出了勤组的使用范围");
        }
        
        int ele = arr[index];
        return ele;
	}
}
  • Objects非空判断

类Objects由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,对对象为null的值进行了抛出异常操作。

public static <T> T requireNonNull(T obj):查看指定引用对象不是null。

查看源码发现这里对为null的进行了抛出异常操作∶

public static <T> T requireNonNull(T obj) {
    if (obj == null)
    	throw new NullpointerException();
    return obj;
}
public class Demo040bjects {
    public static void main(String[]args)i
    	method(null);
    }
    public static void method(Object obj){
        /*
        //对传递过来的参数进行合法性判断,判断是否为nulL
        if(obj == null){
        	throw new NullpointerException("传递的对象的值是null");
        }
        */
        Objects.requireNonNull(obj);
        Objects.requireNonNull(obj,"传递的对象的值是null");
    }
}

  • 声明异常throws

声明异常∶将问题栋识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).

声明异常格式:

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2...{}
/*
    throws关键字:异常处理的第一种方式,交给别人处理
    作用:
        当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
        可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理-->中断处理
	使用格式:在方法声明时使用
		修饰符 返回值类型 方法名(多数列表)throwsAAAExcepiton, BBBExcepiton...{
            throw new AAAExcepiton("产生原因“);
            throw new 888Excepiton("产生原因");
            ...
		}
	注意:
        1.throws关键字必须写在方法声明处
        2.throws关键字后边声明的异常必须是Exception或者是Exception的子类
        3.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常
        	如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
        4.调用了一个声明抛出异常的方法,我们就必须的处理声明的异常
        	要么继卖使用throws声明抛出,交给方法的调用者处理,最终交给JVM
        	要么try...catch自己处理异常
*/
public class Demo05Throws {
    /*
    FiLeNotFoundException extends IOException extends Excepiton
    如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
    */
    //	public static void main(String[]args) throws FileNotEoundException,IOException{
    //public static void main (String[]a) throws IOException {
	public static void main(String[] args)trows Exception{
        //readFile("c:\\a.txt");
        //readFile("d:\\a.txt");//抛出异常
        readFile("d:\\a.tx");
    }
    /*
        定义一个方法,对传递的文件路径进行合法性判断
        如果路径不是"c: ila.txt",那么我们就抛出文件找不到异常对象,告知方法的调用者
        注意:
        FileNotFoundException是编译异常,抛出了编译异常,就必须处理这个异常
    	可以使用throws继续声明抛出FiLeNotFoundException这个异常对象,让方法的调用者处理
    */
    public static void readFile(String fileName) throws FileNotEoundException,IOException{
        if( !fileName.equals("c: lla.txt")){
			throw new FileNotEoundException("传递的文件路径不是c:\\a.txt.");
        }
        
        /*
        如果传递的路径,不是.txt结尾
        那么我们就抛出Io异常对象,告知方法的调用者,文件的后缀名不对
        */
        if(!fileName.endswith(".txt")){
			throw new IOException("文性的后缀名不对");
        }
		
        System.out.println("路径没有回题,读取文件");
    }
}

自己捕获并处理异常

如果异常出现的话,会立刻终止程序,所以我们得处理异常:
1.该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)。
2.在方法中使用try-catch的语句块来处理异常。

try-catch的方式就是捕获异常。
捕获异常 :Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。

捕获异常语法如下:

try{
	编写可能会出现异常的代码
}catch(异常类型e){
	处理异常的代码
	//记录日志/打印异常信息/继续抛出异常
}
/*
    try...catch:异常处理的第二种方式,自己处理异常
    格式:
        try{
            可能产生异常的代码
        }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
            异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            —般在工作中,会把异常的信息记录到一个日志中
        }
        ...
        catch(异常类名变量名){
		}
    注意:
    	1.try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
    	⒉.如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,纶卖执行try...catch之后的代码
		如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try...catch之后的代码
*/
public class Demo01TryCatch {
    public static void main(String[]args){
		try{
			//可能产生异常的代码
			readFile("d:\\a.tx");
		}catch(IOException e){//try中抛出什么异常对象, catch就定义什么异常变量,用来接收这个异常对象
            //异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            System.out.println("catch -传递的文件后缀不是.txt" );
		}
        System.out.println("后续代码");
    }
    /*
    如果传递的路径,不是.txt结尾
    那么我们就抛出Io异常对象,告知方法的调用者,文件的后缀名不对
    */
    public static void readFile(String fileName){
        if(!fileName.endswith(".txt")){
			throw new IOException("文性的后缀名不对");
        }
		
        System.out.println("路径没有回题,读取文件");
    }
}
  • Throwable类中的处理异常方法

如何获取异常信息∶
Throwable类中定义了一些查看方法:
1.public string getMessage() :获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
2.public string tostring():获取异常的类型和异常描述信息(不用)。
3.public void printstackTrace():打印异常的跟踪栈信息并输出到控制台。

包含了异常的类型异常的原因还包括异常出现的位置,在开发和调试阶段都得使用printStackTrace。

在开发中呢也可以在catch将编译期异常转换成运行期异常处理。
多个异常使用捕获又该如何处理呢?l
1.多个异常分别处理。
2.多个异常一次捕获,多次处理。
3.多个异常一次捕获一次处理。

public class Demo01TryCatch {
    public static void main(String[]args){
		try{
			//可能产生异常的代码
			readFile("d:\\a.tx");
		}catch(IOException e){
        /*
         Throwable类中定义了3个异常处理的方法
         String getMessage()返回此throwable的简短描述。
         String toString()返回此throwable的详细消息字符串。
         void printStackTrace() JVM打印异常对象,默认此方法,打印的异常信息是最全面的
        */
         //System.out.println(e.getHessage());//文件的后缀名不对
         //System.out.println(e.toString());//重写object类的toString,java.io.IOException:文件的后缀名不对
          	//System.out.println(e);//java.io.IOException:文件的后缀名不对
			e. printStackTrace();
		}
        System.out.println("后续代码");
    }
    /*
    如果传递的路径,不是.txt结尾
    那么我们就抛出Io异常对象,告知方法的调用者,文件的后缀名不对
    */
    public static void readFile(String fileName){
        if(!fileName.endswith(".txt")){
			throw new IOException("文性的后缀名不对");
        }
        System.out.println("路径没有回题,读取文件");
    }
}
  • final代码块
/*
	格式:
        try{
            可能产生异常的代码
        }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
            异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            —般在工作中,会把异常的信息记录到一个日志中
        }
        ...
        catch(异常类名变量名){
		}final{
			无论是否出现异常,都会执行
		}
	注意:
        1.finalLy不能单独使用,必须和try一起使用
        2.finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)
*/
public class Demo02TryCatchFinally {
    public static void main(String[]args){
		try{
			//可能产生异常的代码
			readFile("c:\\a.ttxt");
			//System.out.println("释放资源");//没有final代码块时,出现异常,这行代码不执行
		}catch(IOException e){
			e. printStackTrace();
		}final{
            System.out.println("释放资源")
        }
        System.out.println("后续代码");
    }
    /*
    如果传递的路径,不是.txt结尾
    那么我们就抛出Io异常对象,告知方法的调用者,文件的后缀名不对
    */
    public static void readFile(String fileName){
        if(!fileName.endswith(".txt")){
			throw new IOException("文性的后缀名不对");
        }
        System.out.println("路径没有回题,读取文件");
    }
}
  • 异常注意事项

    • 多个异常使用捕获又该如何处理呢?
      1.多个异常分别处理。
      2多个异常一次捕获,多次处理。
      3.多个异常一次捕获一次处理。
      一般我们是使用一次捕获多次处理方式,格式如下:
    try{
    	编写可能会出现异常的代码
    }catch(异常类型A e){try中出现A类型异常,就用该catch来捕获.
    	处理异常的代码
    	//记录日志/打印异常信息/继续抛出异常
    }catch(异常类型Be){try中出现B类型异常,就用该catch来捕获.
    	处理异常的代码
    	//记录日志/打印异常信息/继续抛出异常
    }
    
    public class Demo01Exception {
        public static void main(String[]args) {
            /*
            多个异常使用捕获又该如何处理呢?
                1.多个异常分别处理。
                2.多个异常一次捕获,多次处理。
                3.多个异常一次捕获一次处理。
            */
            /*
            //1.多个异常分别处理。
            try {
                int[]arr = {1,2,3};
                System.out.printin(arr[3]);//ArrayIndexoutOfBoundsException: 3
            }catch (ArrayIndexoutOfBoundsException e)i
            	System.out.println(e);
            try{
                List<Integer> list = list.of(1,2,3);
                System.out.println(list.get(3));//IndexoutOfBoundsException:Index 3 out-of-bounds for length 3
            }catch (IndexoutofBoundsException e){
                System.out.println(e);
            }*/
            /*
            //2.多个异常一次捕获,多次处理。
            try{
                int arr = {1,2,3};
                System.out.println(arr[3]);//ArrayIndexoutOfBoundsException: 3
                List<Integer> list = List.of(1,2,3);
                System.out.println(list.get(3));//IndexoutOfBoundsException: Index 3 out-of-bounds for length 3
            }catch (ArrayIndexoutOfBoundsException e){
                //注意这里,因为数组越界,先出现异常,因此集合越界异常那不会显示,因为程序跳过了
            	System.out.println(e);
            }catch (IndexoutofBoundsException e){
            	System.out.println(e);
            }
        /*
         一个try多个catch注意事项
         	catch里边定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上边,否则就会报错(ArrayIndexoutOfBoundsException和IndexoutofBoundsException就会报错)
            */
            */
                /*
            //3.多个异常一次捕获一次处理。
            try {
                int[]arr = {1,2,3};
                // system.out.println(arr[3]);//ArrayIndexOutofBoundsException: 3
                List<Integer> list = List.of(1,2,3);
                system.out.println(list.get(3));//IndexoutOfBoundsException: Index 3 out-of-bounds for length 3
            }catch (IndexoutofBoundsException e){
            	System.out.println(e);
        	}
        	*/
                
            //运行时异常被抛出可以不处理。即不捕获也不声明抛出。
    		//扔给虚拟机处理,终止程序,什么时候不抛出运行时异常了,在来继续执行程序
            int[]arr = {1,2,3};
            System.out.println(arr[3]);//ArrayIndexoutofBoundsException: 3
            List<Integer> list = List.of(123);
            System.out.println(list.get(3));//IndexoutOfBoundsException: Index 3 out-of-bounds for length 3
            
            System.out.println("后续代码!");
        }
    }
    

针对第二种捕获多异常方式,try中可能会产生的异常对象:
new ArrayIndexOutOfBoundsException("3");
new IndexOutOBoundsException("3);
try中如果出现了异常对象,会把异常对象抛出给catch处理
抛出的异常对象,会从上到下依次赋值给catch中定义的异常变量

ArrayIndexoutOfBoundsException e  = new ArraylndexOutOfBoundsException("3");
IndexoutofBoundsException e=new IndexOutOfBoundsException("3) 

IndexoutofBoundsException e=new ArrayindexOutOfBoundsException("3");多态
IndexoutofBoundsException e=new IndexOutOfBoundsException("3);
这样导致数组越界异常catch没用

注意事项:

  1. 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
  2. 如果finally有return语句,永远返回finally中的结果,避免该情况.
  3. 如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集。
  4. 父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
  5. 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
/*	注意事项2:
	如果finally有return语句,永远返回finally中的结果,避免该情况.
*/
public class Demo02Exception {
	public static void main(String[]args) {
		int a = getA();
		System.out.println(a);//永远是a=100
	}
	//定义一个方法,返回变量a的值
    public static int getA(){
        int a = 10;
        try{
        	return a;
        }catch (Exception e){
        	System.out.printin(e);
        }finally {
        //一定会执行的代码
        a=100;
        return a;
    }
}
/*	注意事项3:子父类异常
	子父类的异常:
	如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
	父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明我出
    注意:
    父类异常时什么样,子类异常就什么样
*/
public class Fu {
    public void show01() throws NullPointerException,classcastException(){}
    public void show02() throws IndexOutOfBoundsException{}
	public void show03() throws IndexOutOfBoundsException{}
	public void show84(){}

}

class zi extends Fu{
    //子类重写父类方法时,抛出和父类相同的异常
    public void show01() throws NullPointerException,classCastException{}
	//子类重写父类方法时,抛出父类异常的子类
    public void show02() throws ArrayIndexoutOfBoundsException{}
    //子类重写父类方法时,
    public void showe3(){}
	
	//父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。
	//public void show04( ) throws Exception{}

	//此时子类产生该异常,只能捕获处理,不能声明抛出
    public void showe4(){
        try {
            throw new Exception("编译期异常");
        } catch (Exception e) {
            e.printstackTrace( );
        }
    }
}
  • 自定义异常
/*
	自定义异常类:
    	java提供的异常类,不够我们使用,需要自己定义一些异常类
    格式:
		public class xxxExcepiton extends Exception / RuntimeException{
            添加一个空参数的构造方法
            添加一个带异常信息的构造方法
		}
	注意:
        1.自定义异常类一般都是以Exceptior结尾,说明该类是一个异常类
        2.自定义异常类,必须的继承Exception或者RuntimeException
        	继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
			继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try ...catch
*/
public class RegisterException {
	//添加一个空参数的构造方法
    public RegisterException( ){
    }
	/*
	添加一个带异常信息的构造方法
    查看源码发现,所有的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息
	*/
    public RegisterException(String message){
		super(message);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值