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接口。
Collection
中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。Map
中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的
值。Collection
中的集合称为单列集合,Map
中的集合称为双列集合。- 需要注意的是,
Map
中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
特点:
1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
3.Nap集合中的元素,key是不允许重复的,value是可以重复的
4.Map集含中的元素,key和value是——对应
- Map的子类
- HashMap<K,V> ∶存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
- 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集合常用方法:
public V put(K key,V value)
:把指定的键与指定的值添加到Map集合中。public V remove(Object key)
:把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。public V get(Object key)
根据指定的键,在Map集合中获取对应的值。boolean containsKey(Object key)
判断集合中是否包含指定的键。public Set<K> keySet()
:获取Map集合中所有的键,存储到Set集合中。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.Error
与java.lang.Exception
,平常所说的异常指java.lang.Exception
。
Throwable体系:
- Error : 严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
- Exception :表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒,阑尾炎
常用方法
public void printStackTrace()
:打印异常的详细信息。包含了异常的类型异常的原因还包括异常出现的位置,在开发和调试阶段都得使用printStackTrace。public String getMessage()
:获取发生异常的原因。提示给用户的时候就提示错误原因。public String tostring()
:获取异常的类型和异常描述信息(不用)。
出现异常,可以把异常的简单类名,拷贝到API中去查。
- 异常分类
异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?
- 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化常)
- 运行时期异常: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(1,2,3); 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没用
注意事项:
- 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
- 如果finally有return语句,永远返回finally中的结果,避免该情况.
- 如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集。
- 父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
- 在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);
}
}