Java编程自学之路:方法与异常


Java方法

Java方法(也叫函数)是一段可重用的代码片段;

方法定义

方法定义语法格式:

[修饰符] 返回值类型  方法名([参数类型 参数名称]...) {
  方法体;
  return 返回值;
}
  • 修饰符:修饰符是可选的,它桃酥编译器如何调用该方法,定义了方法的访问类型;
  • 返回值类型:返回值类型表示方法执行结束后,返回结果的数据类型;如果没有返回值,返回值类型为void
  • 方法名:是方法的实际名称;方法名和参数列表共同构成方法签名;
  • 参数类型:参数像是占位符;当方法被调用时,传递值给参数;参数列表包括参数类型、顺序及个数;参数是可选的,方法可以不包含任何参数;
  • 方法体:方法体包含具体的代码片段,实现该方法的功能;
  • return:用于声明方法返回值;返回值类型需与方法定义返回值类型保持一致;在void方法中,return可省略;

方法参数

在C/C++等编程语言中,方法的参数传递一般有两种形式:

  • 值传递:值传递的参数被称为形参;值传递时,传入的参数,在方法中的修改,不会在方法外部生效;
  • 引用传递:引用传递的参数被称为实参;引用传递时,传入的参数,在方法中修改,会在方法外部生效;

在Java中,只支持值传递:

  • 基本类型:会直接拷贝值传递到方法中;
  • 引用类型:拷贝当前对象的引用地址,然后将该地址传递过去,实质上仍然是值传递,只是传递的值是引用地址;

方法修饰符

Java方法有以下几类修饰符:

访问控制修饰符

访问控制修饰符用于定义方法的访问权限控制,权限大小次序为:

public > protected > 包访问权限(默认权限,无关键字)  > private
  • public:表示任何类都可以访问;
  • protected:表示子类可以访问,此外,同一个包内的其他类也可以访问;
  • 包访问权限:默认权限,无关键字,表示当前包中所有类都可以访问;
  • private:表示其它任何类都无法访问;

静态修饰符

静态修饰符关键字为static,被其修饰的方法为静态方法;

静态方法相比于普通方法,主要有以下区别:

  • 支持类名.方法名方式调用,也支持对象名.方法名调用;
  • 静态方法访问本类成员时,只允许访问静态成员,不允许访问实例成员变量及方法;

静态方法常被用于各种工具类、工厂方法类;

最终修饰符

最终修饰符关键字为final,其修饰的方法不能被子类覆写;其修饰的变量赋值后不允许变更;

默认修饰符

默认修饰符关键字为default,JDK8中引入,支持在interface中定义默认方法;default方法只能出现在interface中;

接口中被default修饰的方法被称为默认方法,实现此接口的类如果没有被Override,则直接继承这个方法,不强制要求实现此方法;

抽象修饰符

抽象修饰符关键字为abstract,其修饰的方法称为抽象方法,该方法不能有实体;抽象方法只能出现在抽象类中;

同步修饰符

同步修饰符关键字为synchronized,常用于并发编程;被synchronized修饰的方法在一个时刻,只允许一个线程执行;

特殊方法

Java中,存在一些较为特殊的方法,分别适用于特殊的场景;

main方法

Java中的main方法是一种特殊的静态方法,因为所有的Java程序都是由public static void main(String[] args)方法开始执行,其中args用来接收命令行传入参数,该数组下表从0开始;

构造方法

任何类都有构造方法,构造方法的作用就是在初始化类实例时,设置实例的状态;

每个类都有构造方法,如果没有显示的定义任何构造方法,Java编译器将自动为该类提供一个默认构造方法;

在创建一个对象的时候,至少要调用一个构造方法;构造方法名称必须与类同名,一个类可以有多个不同参数列表的构造方法;

构造方法使用private修饰时,类无法调用此构造方法进行对象实例化,一般用于单例模式;

变参方法

JDK5开始,Java支持传递同类型的可变参数给方法;在方法声明中,指定参数类型后加一个省略号...来定义变长参数;

一个方法有且仅有一个变长参数,且该参数必须为参数列表中的最后一个参数,普通参数必须在变长参数之前生命;

finalize()方法

finalize()在对象被垃圾收集器回收之前调用,用来清除回收对象;

finalizejava.lang.Object中定义,所以每个对象都具有该方法;该方法在GC启动,该对象会回收时调用;

应该尽量避免显示调用finalize()方法;其线程会与主线程产生资源竞争,虽然其优先级较低,但仍然可能触发OOM异常;

方法覆写与重载

覆写(Override)是指子类定义了与父类中同名的方法,但是在方法覆写时必须考虑到访问权限,子类覆写的方法不能拥有比父类更加严格的访问权限;

子类覆写父类方法时,如果需要访问父类方法,使用super关键字即可;

重载(Overload)是指方法名相同,但参数列表不同,通过传递不同的参数来完成不同功能的方法调用;

异常

异常就是有异于常态,和正常情况不一样,有错误出现。在Java中,组织当前方法或作用域的情况,称之为异常;

异常框架

Throwable

Throwable是Java语言中所有错误(Error)和异常(Exception)的超类;在Java中只有Throwable类型的实例才可以被抛出或捕获,它是异常处理机制的基本组成类型;

Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()方法用于获取堆栈跟踪数据等信息;

主要方法:

  • FillInStackTrace:用当前的调用栈层次填充Throwable对象栈层次,添加到栈层次任何先前信息中;
  • getMessage:返回关于发生的异常的详细信息;
  • getCause:返回一个Throwable对象代表异常原因;
  • getStackTrace:返回一个包含对栈层次的数组;
  • printStackTrace:打印toString()结果和栈层次到System.err,即错误输出流;
  • toString:使用getMessage()的结果返回代表Throwable对象的字符串;

Error

ErrorThrowable的一个子类;Error表示正常情况下,不大可能出现的严重问题;编译器不会检查Error;绝大部分的Error都会导致程序处于非正常的、不可恢复状态;

常见Error

  • AssertionError:断言错误;
  • VirtualMachineError:虚拟机错误;
  • UnsupportedClassVersionError:Java类版本错误;
  • StackOverflowError:栈溢出错误;
  • OutOfMemoryError:内存溢出错误;

Exception

ExceptionThrowable的一个子类;Exception表示合理的应用程序可能想要捕获的条件;Exception是程序正常运行过程中,可以预料的以外情况,可能应该被捕获,进行相应处理;

Exception分为可检查(checked)和不可检查(unchecked)异常,可检查异常在源码中必须显示地进行捕获处理,这是编译器检查的一部分;

常见Exception

  • ClassNotFoundException:加载类时,找不到相应的类,抛出该异常;
  • CloneNotSupportedException:调用Object类中的clone()方法克隆对象,但该类未实现Cloneable接口时,抛出该异常;
  • IllegalAccessException:拒绝访问一个类时,抛出该异常;
  • InstantiationException:当试图使用Class类中的newInstance方法创建一个类的实例,而指定的类对象是一个接口或抽象类时,抛出该异常;
  • InterruptedException:一个线程被另一个线程中断,抛出该异常;
  • NoSuchFieldException:请求的变量不存在时抛出该异常;
  • NoSuchMethodException:请求的方法不存在时抛出该异常;

RuntimeException

RuntimeExceptionException的一个子类,可能在Java虚拟机正常运行期间抛出的异常的超类;

常见RuntimeException

  • ArrayIndexOutOfBoundsException:用非法索引访问数组时抛出的异常,如果索引为负或大于等于数组大小,抛出该异常;
  • ClassCastException:试图将错误类型的对象存储到一个对象数组时,抛出该异常;
  • IllegalArgumentException:向方法传递了一个不合法或不正确的参数时,抛出该异常;
  • IllegalMonitorStateException:某一线程试图等待对象的监视器或试图通知其他正在等待对象的监视器而本身没有指定监视器的线程,抛出该异常;
  • IllegalStateException:Java环境或应用程序没有处于请求操作所要求的的适当状态时,抛出该异常;
  • IllegalThreadStateException:线程处于没有请求操作所要求的适当状态时,抛出该异常;
  • IndexOutOfBoundsException:指示某排序索引超出范围时,抛出该异常;
  • NegativeArraySizeException:应用程序试图创建大小为负的数组,抛出该异常;
  • NullPointerException:应用程序试图在需要对象的地方使用null时,抛出该异常;
  • NumberFormatException:应用程序视图将字符串转换为数值类型,但该字符串不能转换为适当格式时,抛出该异常;
  • SecurityException:由安全管理器抛出的异常,指示存在安全侵犯;
  • StringIndexOutOfBoundsException:此异常由String方法抛出,指示索引为负或者超出字符串大小时,抛出该异常;
  • UnsupportedOperationException:当不支持请求的操作时,抛出该异常;

自定义异常

自定义一个异常类,只需要继承ExceptionRuntimeException即可;

抛出异常

如果想在程序中明确地抛出异常,需要使用关键字throwthrows

throwthrows的区别:

  • throws使用在函数上,throw使用在函数类;
  • throws后面跟异常类,可以跟多个,用逗号分割;throw后面跟的是异常对象;

捕获异常

使用try catch关键字可以捕获异常;try catch代码块放在异常可能发生的地方;

JDK7以后,catch支持通过|一次性处理多个异常;

try {
  //可能发成异常的代码块
} catch ( Exception1 | Exception2) {
  //捕获并处理try抛出的异常
} finally {
  //无论是否发生异常,豆浆执行的代码
}

异常链

异常链是以一个异常对象为参数构造新的异常对象,新的异常对象将包含之前的异常信息;

通过使用异常链,我们可以提高代码的可理解性,系统的可维护性和友好性;

异常注意事项

finally覆盖异常

Java异常处理中finally中的return会覆盖catch代码块中的return语句和throw语句,所以Java不建议在finally中使用return语句;

覆盖抛出异常的方法

当子类重写父类带有throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内–用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法。这是为了支持多态。

异常和线程

如果Java程序只有一个线程,那么没有被任何代码处理的异常就会导致程序终止。如果Java程序是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程终止。

最佳实践

  • 对可恢复情况使用检查性异常,对编程错误使用运行时异常
  • 优先使用Java标准的异常
  • 抛出与抽象相对应的异常
  • 在细节消息中包含能捕获失败的信息
  • 尽可能减少try代码块的大小
  • 尽量缩小异常范围。不要忽略异常,一旦捕获异常,就应该处理而非丢弃
  • 异常处理效率很低,所以不要用异常进行业务逻辑处理
  • 各类异常必须要有单独的日志记录,将异常分级、分类管理。
    • 逻辑异常:用于描述业务无法按照预期的情况处理下去,属于用户制造的意外
    • 代码错误:用于描述开发代码错误,例如NPE、ILLARG等,都属于程序员制造的Bug
    • 专有异常:用于特定业务场景,表述指定作业出现以外情况无法预先处理

文章作者: Semon
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Semon !
评论
  目录