Java基础查漏补缺(个人向)

关键字

不熟悉的关键字
  • instanceof
  • throw(手动的生成一个异常)
  • throws(处理异常的方式)
  • native
  • strictfp
  • transient
  • volatile
  • assert
this关键字的使用
  • this可以调用构造器

    • class Person{
          String name;
          int age;
          //空参构造器
          public Person(){}
          //初始化name的构造器
          public Person(String name){
              this();//调用空参构造器
              this.name=name;
          }
          //初始化name和age的构造器
          public Person(String name,int age){
              this(name);//调用初始化name的构造器
              this.age = age;
          }
      }
      
    • 在类的构造器中,可以显式的使用"this(形参列表)"的方式调用本类中指定的其他构造器

    • 但是方法中不能调用本类的构造器

    • 本类中构造器调用构造器,不会创建多个对象

    • "this(形参列表)"必须声明在当前构造器的首行,这也意味着一个构造器只能调用一个构造器

    • this.属性

      • 先从本类中的成员变量去找,如果找不到的话就去父类里找
super关键字的使用
  • “super();”的使用,必须声明在子类构造器的首行
    • 这意味着,在类的构造器中,this(形参列表)和super(形参列表)只能二选一
  • 如果构造器里不去显式的声明this(形参列表)或super(形参列表),那么系统默认分配super(形参列表)
return关键字的使用
  • 返回类型为void的方法,可以加return,但是只能加“return;”,作用是结束方法,return后面不能跟数据
protecte关键字的使用
  • 不同包,但是是该类的子类,也可以调用
package关键字的使用
  • 声明在每个.java文件的首行
  • 同一个包下,不能命名同名的接口、类,
    • 这种情况是不允许的:
    • 定义一个接口名字为test1,同时定义一个类也叫test1
instanceof关键字的使用
  • man instanceof Man;
    
    • 判断man是否是Man类型的实例,如果是返回true,不是返回false

    • man instanceof Object;//答案是正确的,因为Object是所有类的直接父类或间接父类
      
final关键字的使用
  • final可以用来修饰:类、方法、变量
    • 修饰类:
      • 不能被继承
      • 比如String类、System类、StringBuffer类等
    • 修饰方法:
      • 不能被重写
      • 比如:Object类中的getClass()方法
    • 修饰变量:
      • 被final修饰就成为了一个常量
      • 被final修饰必须要对其进行赋值
      • 被final修饰的变量可以考虑赋值的位置有:显式初始化、代码块初始化、构造器初始化
        • 不能用方法初始化的原因:如果方法没有被调用,则final修饰的变量就没有被初始化
    • 修饰局部变量:
      • final修饰形参:
        • 此形参就是一个常量,只能在方法内进行调用,不可以对其进行修改
abstract关键字的使用
  • 不能修饰私有方法、静态方法、final的方法、final的类

Java命名规范

  • 常量名:所有字母都大写。多单词时用下划线连接:XXX_YYY_ZZZ
  • java采用unicode字符集,因此标识符可使用汉字声明,但不建议

变量

  • 成员变量
    • 实例变量(不以static修饰)
    • 类变量(以static修饰)
  • 局部变量除形参外,需要显示初始化
short类型
  • 占用2字节,表数范围-215 ~215 -1
float类型
  • float类型占用4个字节,但表示的为-3.403E38 ~ 3.403E38,原因是该数据类型的表示方法为科学计数法
  • float变量在初始化时后面要加f,因为浮点数默认的数据类型是double
char类型
  • char类型直接使用 Unicode 值来表示字符型常量:格式为:‘\uXXXX’。XXXX代表 十六进制整数,\u000a 表示 \n
  • char类型可进行运算。因每个字符对应有Unicode码
  • char类型不能为空,有且只有一个非空字符。空格可以放
boolean类型
  • boolean类型数据只允许取值true和false,无null
  • 不可以使用0或非0的整数替代false和true,这点和C语言不同
  • Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达所操作的boolean值,在编译之后都使用java虚拟机中的int数据类型来代替:true用1表示,false用0表示
基本数据类型转换
  • 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算
  • byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型
  • 当把任何基本数据类型的值和字符串(String)进行连接运算时(+),基本数据类型的值将自动转化为字符串(String)类型

逻辑运算符

%取余运算
  • m%n,m称为被模数,n称为模数
  • m%n的运算结果只和m有关
  • 取模运算的结果不一定总是整数:12.5%5=2.5
比较运算符
  • 比较运算符的结果都是boolean型,也就是要么是true,要么是false
逻辑运算
  • 逻辑运算符用于连接布尔型表达式,在Java中不可以写成33&x<6\
  • “&”和“&&”的区别:
    • &时,左边无论真假,右边都进行运算
    • &&时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算
  • “|”和“||”的区别:
    • 与“&”和“&&”的区别同理
  • 异或( ^ )与或( | )的不同之处是:当左右都为true时,结果为false。
    • 理解:异或,追求的是“异”!
    • 亦或只有在不同的时候才是true
  • ==的使用:
    • 可用在基本数据类型和引用数据类型之间作比较
      • 用于基本数据类型的作用为比较数值是否相等
      • 用于引用数据类型的作用为比较地址值是否相等
位运算符
  • 效率高
  • TODO
三元运算符
  • 格式:(条件表达式)?表达式1:表达式2
    • 条件表达式==true,结果为表达式1
    • 条件表达式==false,结果为表达式2
  • 三元运算符要求必须返回一个结果
  • 要求表达式1和表达式2必须是同样类型

程序流程控制

switch-case结构
  • switch(表达式)中表达式的值必须是下述几种类型之一:byte,short, char,int,枚举 (jdk 5.0),String (jdk 7.0)
  • case子句中的值必须是常量,不能是变量名或不确定的表达式值
  • 同一个switch语句,所有case子句中的常量值互不相同
  • default子句是可任选的。同时,位置也是灵活的。当没有匹配的case时, 执行default
    • 如果default语句最后没有加break,后面的语句也会执行下去,所以一定要加break
break语句
  • break语句用于终止某个语句块的执行

  • break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块

  • label1: { ……
    	label2: { ……
    		label3: { ……
    				break label2;
    				……
    				}
    			}
    		}
    
  • break只能用于switch语句和循环语句中

continue语句
  • continue语句用于跳过其所在循环语句块的一次执行,继续下一次循环
  • continue只能使用在循环结构中

数组

概述
  • 创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是 这块连续空间的首地址
  • 数组的长度一旦确定,就不能修改
初始化
  • 动态初始化:int[] arr = new int[5];
  • 静态初始化:int[] arr = new int[]{1,2,3,4,5};
数组的初始化
  • 数组是引用类型,其元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素被按照成员变量同样的方式被隐式初始化
    • 对于基本数据类型而言,默认初始化值各有不同
    • 对于引用数据类型而言,默认初始化值为null(注意与0不同!)
    • 各类型的默认初始化值如图
    • image-20220622144024477
数组的内存解析
  • 内存结构简述
    • image-20220622144546831
    • 局部变量在栈空间中
    • 对象在堆空间中
    • 对于引用类型的成员变量,在栈空间中存储的是在堆空间中存储真实数据的地址值。所以像数组、字符串、类等引用型数据类型,存放在栈空间中的是地址值。堆空间中的是真实的值
二维数组
  • 从数组底层的运行机制来看,没有多维数组。

    • int[][] arr1 = new int[3][];
      	    arr1[0]= new int[1];
      	    arr1[1] = new int[5];
      	    arr1[2] = new int[3];
      
    • 如此看来生成的依旧是一维数组,再在一维数组的每个元素赋值上另一个数组的地址值

    • int[][]arr = new int[][3]; 此种声明方式错误

算法的五大特征
  • 输入:有零个或多个输入
  • 输出:至少一个输出
  • 有穷性:在有限的步骤内会自动结束,每一个步骤在可接受的范围内完成
  • 确定性:算法中的每一步都是明确的。不会出现歧义
  • 可行性:算法中的每一步都是清楚可行的,能让用户用纸币计算出结果
数组的常用类
  • Arrays.fill()

    • int[] arr2 = new int[]{1,2,3,4,5,6};
              Arrays.fill(arr2,10);
              System.out.println(Arrays.toString(arr2));
      
    • 输出的结果是[10, 10, 10, 10, 10, 10]

  • Arrays.toString(arr)与arr.toSting的不同(arr为数组)

    • Arrays.toString()返回的是arr内部所存储的值
    • arr.toSting返回的是arr的地址值
  • Arrays.sort():从小打到的排序数组元素

  • Arrays.binarySearch():查找数组中的元素的索引位置

    • 若查到:返回索引值
    • 若未查到:返回负值
注意
  • 数组元素一旦初始化,长度不可变

类和对象

局部变量
  • 局部变量不能添加权限修饰符,如public,private,只能缺省和final
    • 可以理解为局部变量的权限修饰符被方法的权限修饰符代替了
  • 局部变量没有默认初始化值,意味声明局部变量时必须显式赋值
    • 特别的:形参无需赋值,在调用方法时赋值即可
  • 非static修饰的局部变量加载到堆空间中
    • 对比成员变量:成员变量加载到栈空间中,非static局部变量加载到堆空间
方法
  • 在某些语言中也被成为函数或过程
  • 将一些功能封装为方法的目的:实现代码重用,简化代码
  • Java中的方法不能独立存在,必须定义在类里
  • 方法中只能调用方法或属性,不可以在方法内部定义方法
万事万物皆对象
  • java语言本身,将功能、结构等封装到类中,通过实例化成具体的对象来调用具体的功能结构
    • Scanner,String等
    • 文件:File
    • 网络资源:URL
  • 涉及到Java语言与前端HTML、后端的数据库交互时,前后端的结构在与Java层面进行交互时,都体现为类和对象
  • 引用型数据类型在栈空间内存储两种数据
    • null
    • 该对象的地址值(包含该对象的数据类型)
匿名对象
  • new Person().walk();
    new Person().talk();
    
    • 调用匿名对象,上面的两条语句分别创建了两个匿名对象,因为new了两次
  • 因为匿名对象没有名字,所以只能调用一次

方法的重载
  • 名字相同,形参列表不同,形参个数或形参类型or both
可变个数形参的方法
  • 格式:

    • [权限修饰符] [返回值类型] [方法名]([形参类型] ... [形参名]){
          ......
          return [返回值类型];
      }
      
  • 可变形参的个数为[0,+∞),意味着零个可以

  • public void show(String[] strs){}    
    public void show(String ... strs){}
    
    • 以上两个方法在同一个类内部定义会报错,会被编译器认为为同一个方法,所以不能同时出现
  • 采用可变个数形参的方法调用参数的时候和数组的调用一样,也是采用索引的方式

  • 可变个数的形参必须声明在方法参数列表的末尾

  • 可变参数的形参在方法的参数列表中只能有一个

类的加载
  • 只要调用类的内部结构,类就会加载,比如类直接调用类的静态结构时,类就会整个加载

面向对象

封装性
  • 隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
  • 程序设计追求高内聚,低耦合
    • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
    • 低耦合:仅对外暴露少量的方法用于使用
信息的封装和隐藏
  • Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
    • 隐藏一个类中不需要对外提供的实现细节
    • 使用者只能通过制好的方法访问数据,可以适当地加入控制逻辑,限制对属性的不合理操作
    • 便于修改,增强代码的可维护性
权限修饰符
  • image-20220623225531990
  • 四种权限都可以修饰类的内部结构:属性,方法,构造器和内部类
  • 类的修饰:只能用缺省或public,其他的权限修饰符不能修饰类
  • protected:虽然不在同一个包下,但是如果是该类的子类,就可以调用
构造器
  • 任何一个类都有构造器

  • Person person = new Person();
    
    • Person()就是构造器,作用就是创建对象
  • 如果没有显式的定义一个构造器,系统默认分配一个空参构造器

    • 如果定义了带参的构造器,就没有空参的构造器了,如果想用空参的构造器,就需要运用重载,去显式的定义空参的构造器、
JavaBean
  • 一种Java语言编写的可重用组件
  • JavaBean符合如下标准:
    • 类是公共的
    • 有一个无参的公共的构造器
    • 有属性,并有对应的get和set方法
继承性
继承性的作用
  • 继承的出现减少了代码冗余,提高了代码的复用性
  • 继承的出现,更有利于功能的扩展
  • 继承的出现让类与类之间产生了关系,提供了多态的前提
注意
  • 在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集, 而是对父类的“扩展”
  • 不要仅为了获取其他类中某个功能而去继承
  • 子类继承父类,就获得父类的全部的属性和方法,并加载到子类本身的内存中
    • 子类不能调用父类用private修饰的属性和方法,只是因为封装性的原因
  • Java只支持单继承和多重继承,不允许多继承
  • 子类直接继承的父类成为:直接父类。间接继承的成为:间接父类
子类实例化的过程
  • 当new对象时,该对象的类会间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类的空参构造器为止。因为加载过父类的结构,所以才看到内存中有父类的结构,子类对象才可以考虑调用
  • 为什么super()和this()只能出现在第一行?
    • 因为子类想要获取父类的结构,要先加载父类
  • 虽然创建子类对象时调用了父类的构造器,但没有“new”这个关键字,说明并没有创建多个对象,只是加载了父类的结构
    • 所以从这儿个角度看,构造器的作用是加载类的结构,而new的作用是将加载好的类的结构封装起来创建成一个对象
方法的重写
  • 子类重写的方法的返回值类型不大于父类被重写的方法的返回值类型
    • (简记:返回值类型<=父类返回值类型)
    • 也就是说:父类的返回值类型是A,子类的返回值类型要(>=A)
    • 父类返回值类型是void,子类重写的类型也只能是void
    • 父类被重写的方法的返回值类型是基本数据类型,子类重写父类的方法的返回值类型必须和父类的返回值类型相等
  • 子类重写的方法使用的访问权限不小于父类被重写的方法的访问权限
    • (简记:权限修饰符>=父类权限修饰符)
    • 子类不成重写父类中用private修饰的方法,就算子类中定义了和父类同名同参的方法,也不叫重载
    • 记忆方法:子类想要覆写父类的方法,子类就像一张比父类还要大的饼,权限只有更大才能覆盖住父类的方法
  • 子类方法抛出的异常不大于父类被重写方法的异常
    • (简记:异常<=父类异常)
  • 子类重写父类的方法,要保持static修饰符一致性。也就是父类有static修饰,子类也得有。父类没有,子类也得没有
  • 重写和重载的区别
    • 对于重载而言,在方法被调用之前,编译器就已经确定了所要调用的方法,被称为“早绑定”或“静态绑定”
    • 对于多态而言,只有等到方法调用的那一刻,编译器才确定
对继承的自己的理解
  • 子类继承父类,子类获取了父类的所有属性和方法。但是如果子类中没有显式的声明和父类同名的属性或方法时,this的运用都会指向父类,构造器除外。
  • 属性不存在重写这么一说
抽象类
  • 可以修饰:类、方法
    • 修饰类:不可以被实例化
  • 抽象类有构造器,虽然自己不能调用构造器,但是它的子类需要调用抽象类的构造器
  • 开发中,都会提供抽象类的子类,让子类完成实例化
  • 抽象方法中可以没有抽象类
抽象方法
  • 只有方法的声明,没有方法体
    • 意味着抽象方法不能被调用,为了使抽象方法不能被调用,那么该类就不能被实例化,所以拥有抽象方法的类,必须声明为抽象类
    • 反正,抽象方法中可以没有抽象类
多态性
何为多态性?
  • 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
多态的使用:
  • 在编译期间,只能调用父类中声明的方法。

  • 在运行期间,实际执行的是子类重写的方法

  • 多态性的使用前提:

    • 类的继承
    • 方法的重写
  • 对象的多态性只适用与方法,不适用与属性(属性不论在编译还是运行期间都调用和执行的子类的属性)

  • 父类的方法只在编译期间调用,被成为虚拟方法

  • 多态是运行时行为

  • 父类根据赋给它的不同子类对象,动态的调用属于该子类的该方法,此种方法调用在编译期是无法确定的,只有在方法调用的那一刻,解释运行器才会确定所要调用的具体的方法,这被称为“晚绑定”或“动态绑定

  • Person man = new Man();
    
    • 此时man对象不能调用Man类中有但Person类中没有的方法,并不是man没有加载,而是由于赋值给Person对象,所以Man中有,但Person没有的对象被屏蔽了,如果想调用Man中结构,只需要把man类型转换成Man即可
Object类
  • 只声明了一个空参构造器

  • finalize方法:对象在被回收之前会调用finalize方法,我们不要自行的调用该方法,一般都由垃圾回收器自动的调用该方法

  • equals方法:

    • 基本数据类型变量无法调用该方法,因为基本数据类型不是对象

    • 只能用于引用数据类型

    • 在Object类中定义的equals方法和“==”作用是相同的

      • public boolean equals(Object obj) {return (this == obj);}
        
      • 也就是Object中的equals方法作用为比较两个对象的地址值

    • String、Date、File、包装类等都重写了Object类中的equals方法

      • 重写之后比较的就不是地址值是否相等了,而是比较的“实体内容”是否相等
  • toString方法的使用

    • 当我们输出一个对象的引用(全类名+内存地址)的时候,也就是调用了当前对象的toString方法
    • String、Date、File、包装类等都重写了Object类中的toString方法
      • 以上类调用toString方法时会输出“实体内容”的值
包装类(封装类)
  • 数值型的包装类有共同的父类Number

    • image-20220625000051243
    • Integer i = new Integer("123");//可以
      Integer j = new Integer("12a");//不可以
      
      • 这样写是可以的,但是一定要注意字符串类型的变量必须是一个数,第二行的写法就是错的
  • Boolean:

    • Boolean b = new Boolean(true);//可以
      Boolean b2 = new Boolean("true");//可以
      Boolean b3 = new Boolean("trUe");//可以
      Boolean b4 = new Boolean("TrueTT");//不可以
      
    • 当布尔包装类中传入的String类型的值时,是忽略大小写的,只要是True就认为是True,知道不是true就是false

    • boolean b;
      Boolean b1;
      
      • b的初始化值为false
      • b1的初始化值为null,因为b1是类,而不是基本数据类型
  • 包装类转化为基本数据类型:

    • 调用包装类Xxx的XxxValue()方法

    • Integer in1 = new Integer(5);
      int in2 = in1.intValue();
      
    • 其它类型同理

  • 自动装箱

    • int num1 = 5;
      Integer in1 = num1;
      
      boolean flag = true;
      Boolean flag1 = flag;
      
      • 其它基本数据类型都一样的用,直接赋值就行
  • 自动拆箱

    • Integer in1 = new Integer(5);
      int num1 = in1;
      
    • 其它数据类型同理

基本数据类型、包装类与String类型的相互转化
  • 基本数据类型、包装类—>String类型

    • int num1 = 5;
      float f1 = 5.0;
      String str1 = String.valueOf(num1);
      String str2 = String.valueOf(f1);
      
    • 调用String.valueOf(参数)方法即可,其它类型同理

  • String类型—>基本数据类型、包装类

    • String str1 = "123";
      int num1 = Integer.parseInt(ster1);
      
    • 调用Xxx.parseInt(传参数)方法即可,其它类型同理

抽象类和抽象方法
  • 只能修饰:类、方法
接口
  • 可以定义全局常量

    • interface TestInterface{
          public static final int MAX_SIZE = 500;
          int MIN_SIZE = 0;
          
          public abstract void fly();
          void flyFly();
      }
      
    • 以上这两种的定义全局常量的方式都是一样的

    • 以上这两种的定义抽象方法的方式都是一样的

  • 不能定义构造器,意味着不能实例化

  • 如果接口的实现类覆盖了接口中是所有方法,则可以该实现类可以进行实例化

    • 如果接口的实现类没有覆盖接口中的所有方法,则该实现类仍为一个抽象类
  • 接口和接口之间可以继承,而且可以多继承

  • jdk8之后可以在接口中定义静态方法和默认(default)方法

    • 接口中的静态方法只能通过接口调用,接口的实现类是不能调用的
    • 通过实现类的对象,可以调用接口中的默认方法
      • 如果接口的实现类重写了接口的默认方法,那么调用的就是重写以后的方法
  • 如果子类继承的父类和实现的接口中声明了同名同参的方法,在子类没有重写该方法的时候,默认调用的是父类的方法

  • 如果子类实现的多个接口中都定义了同名同参的方法,那么实现类要调用时会报错,要解决此问题只能重写该方法了

  • 实现类想要调用实现的接口的默认(default)方法:InterfaceName.super.method();

内部类
  • 分为局部内部类(方法内、代码块内、构造器内)、成员内部类(静态、非静态)

  • //示例代码
    public class Person {
        
        String name;
        int age;
        
        public void walk(){
            System.out.println("我在走路");
        }
        
        class Brain{
            
            String BName = name;//Person.this.name;
            
            public void letWalk(){
                walk();//Person.this.walk();
            }
        }
        
        static class Heart{
            public void show(){
                System.out.println("心脏在跳动")
            }
        }
    }
    
    
成员内部类
  • 作为外部类的成员:

    • 可以调用外部类的结构
      • 如上代码,成员内部类Brain可以调用Person类中的属性和方法
        • 也可以写成Person.this.name,和Person.this.walk()
    • 可以被static修饰,也可被额外的四种权限修饰符修饰(public private protected 缺省的)
  • 如何实例化?

    • //接上文代码
      public class Client{
          public static void main(String[] args){
              //创建Heart实例(静态的成员内部类)
              Person.Heart heart = new Person.Heart();
              heart.show();
              //创建Brain实例(非静态的成员内部类)
              Person p = new Person();
              Person.Brain brain = p.new Brain();
              brain.letWalk();
          }
      }
      
局部内部类
  • 局部内部类的用法1:

    • public class InnerClassTest{
            public Comparable getComparable(){
              class MyComparable implements Comparable{
                  @Override
                  public int compareTo(Object o) {
                      return 0;
                  }
              }
              return new MyComparable();
          }
      }
      
    • getComparable方法的返回值为Comparable接口类型,就在方法内生成一个实现了Comparable的方法,再返回一个实现了该接口的对象

  • 局部内部类的用法2:

    • public class InnerClassTest1{
          return new Comparable() {
                  @Override
                  public int compareTo(Object o) {
                      return 0;
                  }
              }; 
      }
      
    • 是匿名对象的匿名子类

  • 局部内部类要注意的点:

    • public class Person{
          public void method(){
              int num = 10;
              class InnerClass{
                  num=20;
              }
          }
      }
      
    • 以上的写法是错误的!!!!

      • 原因是如果局部内部类InnerClass想要调用method方法中的num变量,则该变量必须声明为final。jdk8以后不写final时会被自动识别成final

代码块(初始化块)

  • 代码块的作用:初始化类或对象
  • 分类:
    • 非静态代码块:无修饰的代码块
    • 静态代码块:static修饰代码块
非静态代码块:无修饰的代码块
  • 随着对象的创建而加载并执行一次

    • 创建一个对象执行一次
  • 作用:

    • 可以对对象的属性等进行初始化
  • 可以定义多个

    • 按照声明的先后顺序执行
  • class Person(){
        {
            i = 6;
        }
        int i = 5;
        System.out.println(i);
    }
    
    • 输出得到的结果是i=5,原因是i首先进行初始化时默认值为0,然后执行非静态代码块里的内容,再执行int i = 5这一行,所以得到的结果是i=5
静态代码块:static修饰代码块
  • 随着类的加载而加载并执行一次,只执行一次
    • 代码块不会被调用,所以要随着类的加载而执行
  • 作用:对类的信息进行初始化
  • 可以定义多个
    • 按照声明的先后顺序执行
      • 静态代码块的执行顺序早于非静态代码块
  • 不可以调用非静态的结构

异常处理

try-catch的使用
  •  	@Test
        public void test1(){
            String str = "123";
            str = "abc";
            try {
                int num = Integer.parseInt(str);
                System.out.println("test++++++++++++++");
            }catch (NumberFormatException e){
                System.out.println("test--------------");
            }catch(Exception e){
                System.out.println(e.getMassage);//打印异常信息
                e.printStackTrace();//打印堆栈信息,此方式较常用
            }
            System.out.println("test结束");
        }
    
  • test--------------
    test结束
    
    • 由此输出结果可以看出,没有输出"test++++++++++++++",原因是出现异常后,跑去执行catch语句,一场后面的语句就不执行了
    • 有多个catch的时候,也只执行一个catch
    • catch中的异常类型没有子父类关系,则声明的顺序随意
    • catch中的异常类型有子父类关系,子类要声明在父类前,否则报错
  • 在try结构中声明的变量,在出了try结构后,就不再被调用

finally的使用
  • finally中声明的是一定会被执行的代码,即使catch中出现了异常,try中有return语句,catch中有return语句

  •     @Test
        public void testMethod(){
            int num = method();
            System.out.println(num);
        }
    
        public int method(){
            try {
                int[] arr = new int[10];
                System.out.println(arr[10]);
                return 1;
            }catch (ArrayIndexOutOfBoundsException e){
                e.printStackTrace();
                return 2;
            }finally {
                System.out.println("+++++++++++++");
            }
        }
    
  • //执行结果
    java.lang.ArrayIndexOutOfBoundsException: 10
    	at java5.ExceptionTest1.method(ExceptionTest1.java:33)
    	at java5.ExceptionTest1.testMethod(ExceptionTest1.java:26)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:134)
    	at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:597)
    	at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:173)
    	at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
    	at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:816)
    	at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:146)
    	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
    	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
    	at java.util.ArrayList.forEach(ArrayList.java:1259)
    	at org.testng.TestRunner.privateRun(TestRunner.java:766)
    	at org.testng.TestRunner.run(TestRunner.java:587)
    	at org.testng.SuiteRunner.runTest(SuiteRunner.java:384)
    	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:378)
    	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:337)
    	at org.testng.SuiteRunner.run(SuiteRunner.java:286)
    	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
    	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
    	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1187)
    	at org.testng.TestNG.runSuitesLocally(TestNG.java:1109)
    	at org.testng.TestNG.runSuites(TestNG.java:1039)
    	at org.testng.TestNG.run(TestNG.java:1007)
    +++++++++++++
    	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
    	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)
    2
    
    • 这里的流程是先把finally里面声明的语句执行完毕之后,再返回值2
  •    @Test
        public void testMethod(){
            int num = method();
            System.out.println(num);
        }
    
        public int method(){
            try {
                int[] arr = new int[10];
                System.out.println(arr[10]);
                return 1;
            }catch (ArrayIndexOutOfBoundsException e){
                e.printStackTrace();
                return 2;
            }finally {
                System.out.println("+++++++++++++");
                return 3;
            }
        }
    
  • //运行结果
    java.lang.ArrayIndexOutOfBoundsException: 10
    	at java5.ExceptionTest1.method(ExceptionTest1.java:33)
    	at java5.ExceptionTest1.testMethod(ExceptionTest1.java:26)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:134)
    	at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:597)
    	at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:173)
    	at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
    	at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:816)
    	at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:146)
    	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
    	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
    	at java.util.ArrayList.forEach(ArrayList.java:1259)
    	at org.testng.TestRunner.privateRun(TestRunner.java:766)
    	at org.testng.TestRunner.run(TestRunner.java:587)
    	at org.testng.SuiteRunner.runTest(SuiteRunner.java:384)
    	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:378)
    	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:337)
    	at org.testng.SuiteRunner.run(SuiteRunner.java:286)
    	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
    	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
    	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1187)
    	at org.testng.TestNG.runSuitesLocally(TestNG.java:1109)
    	at org.testng.TestNG.runSuites(TestNG.java:1039)
    	at org.testng.TestNG.run(TestNG.java:1007)
    	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
    	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)
    +++++++++++++
    3
    
throws的使用
  • 使用throws将异常抛出,异常后的代码就不执行了

  • /*
     * 方法重写的规则之一:
     * 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
     */
    public class OverrideTest {
    	
    	public static void main(String[] args) {
    		OverrideTest test = new OverrideTest();
    		test.display(new SubClass());
    	}
    
    	public void display(SuperClass s){
    		try {
    			s.method();//①
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    class SuperClass{
    	public void method() throws IOException{	
    	}
    }
    
    class SubClass extends SuperClass{
    	public void method()throws FileNotFoundException{
    	}
    }
    
    • 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型的原因是多态
      • 首先看到,①被调用时,是按照SuperClass类里的method方法抛出的异常类型所捕获的,但是display方法的形参列表是SuperClass,这意味着传入的参数可以是SuperClass的子类,而调用子类的method方法,同样要捕获异常,如果子类所抛出的异常的类型大于父类的异常,那么以上的代码就会出现错误,所以为了维护多态性,子类重写父类的方法所抛出的异常类型小于等于父类方法所抛出的异常类型
  • 父类中的方法没有抛出异常,则子类重写父类的方法也不能抛出异常。如果存在异常,就要用try-catch-finally的方式处理

throw的使用
  • public class StudentTest {
    	public static void main(String[] args) {
    		try {
    			Student s = new Student();
    			s.regist(-1001);
    			System.out.println(s);
    		} catch (Exception e) {
    			System.out.println(e.getMessage());
    		}
    	}	
    }
    
    class Student{
    	private int id;	
    	public void regist(int id) throws Exception {
    		if(id > 0){
    			this.id = id;
    		}else{
    			throw new Exception("不能输入负数");
    		}
    	}
    }
    
    • 手动的抛出异常需要使用关键字throw
自定义异常
public class MyException extends Exception{

    static final long serialVersionUID = -3387516993124229958L;

    public MyException(String message){
        super(message);
    }
}

1.要么继承RuntimeException,要么继承Exception

  • 继承RuntimeException就不需要显式的去处理了

2.提供全局常量:serialVersionUID(相当于唯一标识该类的标记)

其他

对main方法的简单理解
  • public作为修饰符的原因:main方法的调用时jvm调用的,调用的时候就不在当前包里调用了,所以权限要设置的大些
  • static作为修饰关键字的原因:main方法需要直接从类中调用,所以要用static修饰
  • void作为返回值类型的原因:main方法只被jvm调用,所以不需要返回值
  • 形参是数组的原因:作为和控制台交互的方式
创建匿名子类的匿名对象
  • public class Client{
        public static void main(String[] args){
            method(new Person(){
                @Override
                public void walk(){
                    System.out.println("匿名子类的匿名对象");
                }
            });
        }
        
        public static void method(Person p){
            p.walk();
        }
    }
    
    class Person{
        public void walk(){
            System.out.println("走路");
        }
    }
    

你可能感兴趣的:(Java基础,java,开发语言)