包装类
通常我们定义参数的时候,我们直接使用数据类型定义,但是为了方便操作,Java为其中的八种基本数据类型都添加了包装类
基本数据类型 | 包装类 |
---|---|
int | Integer |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
byte | Byte |
short | Short |
long | Long |
其中的数字类包装类,Integer,Short,Long,Double,Float,Byte都是Number的子类
而Character,Boolean则是Object的直接子类
通过包装类,我们能更新定义参数的方法:
int a = 10;
Integer a = new Integer(10);
其中将10包装为Integer的操作被称为装箱操作,而将Integer转为int类型则是拆箱操作
但是当我们使用下面的代码时,会出现这样的情况
这就是意味着,这个方法已经过时了,推荐你不要使用,目前Java已经提供了自动装箱和自动拆箱的操作:
Float f1 = 12.34f;// 自动装箱
float f2 = f1; // 自动拆箱
此时代码也没有安分,你可以看到:
意思是使用Float的原始形态float就可以了,这是因为我们还没有用Float进行任何操作,是正常现象
包装类还有一个非常使用的部分,即字符串转化,在输入一个字符串时,可以很方便的将该字符串转为你想要的类型:
int x = Integer.parseInt(“123”);
float y = Float.parseFloat(“12.23”);
boolean z = Boolean.parseBoolean(“false”);
…
(特别注意:转化过程中如果传入abc强行转为int依然会出错,boolean的转化只有输入ture(无视大小写)才会返回true,其他情况都是false)
异常处理
异常指在程序中导致程序中断的一种指令流,在异常发生时,程序将会中断,所有后续程序都无法执行。
在Java中,当产生异常时,实际上是Java自动产生了一个异常类的对象,我们想要对异常进行处理,让程序继续运行下去,则需要捕获该异常
Java中有很多异常类,而异常的顶级父类是Throwable类,其有两个子类分别为error类和Exception类,Exception类又有一个主要子类RuntimeException。
如果出现Error类的异常,即Java认为你无法解决的错误,大多和硬件有关,无法通过代码解决,只能考虑避免(如内存不足)
而Exception类中的异常类,是受检异常,就是这种异常在编译时就会出现错误提示,提示你这里有问题;其中的RuntimeException又叫做非受检异常,即运行时可能发生的错误
try-catch
在之前我讲过简单的异常处理,现在我们来学习更高级的异常处理方式try-catch:
try{
// 可能产生异常的代码段
}catch(异常类型1 对象名1) {
// 异常的处理操作1
}catch(异常类型2 对象名2) {
// 异常的处理操作2
}...
finally{
// 异常的统一出口
}
当try语句中产生异常时,系统会自动产生一个异常类的实例化对象,并且自动找到匹配的catch语句,所有的catch根据方法的参数匹配异常类的实例化对象(所以你必须知道会出现什么样的错误),如果匹配成功,则立即进行catch中的代码
在进行异常的处理之后(无论是否有异常),会执行finally中的代码(除非使用System.exit(0);强制结束),即使在之前的catch中return了,也会先执行finally中的代码再进行return(return的部分会被储存,finally不能改变return的值,但是如果是return一个对象中的属性,finally可以改变该属性)
让我们看一段代码
import java.util.Scanner;
public class Exception {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int a = 120;
int b = scanner.nextInt();
System.out.println(a / b);
System.out.println("1");
}
}
这一整段代码都不会有任何错误提示,但是在运行时可能出现两种异常,一是你输入了一个非int类型的参数,二是你输入了0,无论哪一种,都会立即使该程序停止,后面的1也不会被输出
我们分别进行错误操作,就可以看见错误的原因
所以我们用try-catch将这段代码进行处理
public class Exception {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int a = 120;
try {
int b = scanner.nextInt();
System.out.println(a / b);
System.out.println("Run successfully");
}
catch (InputMismatchException e){
System.out.println("Please input an integer");
}
catch (ArithmeticException e){
System.out.println("Divisor cannot be 0");
}
}
}
这时无论怎么输入,整个程序都不会崩溃,在出现错误的时候还能继续运行并且输出错误的原因(finally可以省去,catch也可以省去,但是两者必须存在至少一个)
当然我们也可以省略一点,将多个catch合并为一个,这时报错并不会显示具体错误原因,只是告诉你出错了(使用Exception类也是一样)
(特别注意:如果想要使用Exception必须写为java.lang.Exception)
catch (RuntimeException e){
System.out.println("Error");
}
JDK7以后也有这种方式,这种方式并不实用,了解即可
catch (InputMismatchException | ArithmeticException e){
System.out.println("Error");
}
throws
throws是给方法的声明的使用,意为不处理该异常,而是将方法抛出去给调用方法的地方,如果抛到最后没有try-catch方法捕获该异常,最后还是会异常终止程序(在出现异常后,后面的代码同样无法被执行)
通过如下方式使用:
返回值 方法名称() throws Exception{
// 方法体
}
其中也可以使用Exception类的子类,如RuntimeException,IOException等等
在程序中也可以使用throw直接抛出异常
throw new Exception(“抛出异常”);
使用该方法相当于主动生成一个异常并抛出,其后面的代码无法被执行
自定义异常类
在之前Object类中,可以通过重写的方式更改默认的输出和判断,在异常类中也可以通过继承现有的异常来构建自定义异常
如果编写一个类继承Exception,并重写一参构造方法,便可以自己定义一个受检类型异常;而继承RuntimeException则是不受检类型异常
可变参数方法和递归
可变参数方法
在创建方法时,如果你不知道传入参数的多少,可以使用一个可变参数方法,无论传入多少参数都可以运行(甚至不传),注意可变参数的部分只能出现在参数列表的最后
返回值类型 方法名称(参数类型... 参数名称){
// 方法体,传入的参数通过数组形式调用
}
int getSum(int... nums){
int sum = 0;
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
但是很尴尬的是不定参方法并没有被广泛使用,和三目运算符一样,虽然很简便,但是并不是不可或缺,而且还被认为破坏了代码逻辑的浪漫
递归
递归是一个算法中的概念,在计算机领域中通常被认为是一个方法调用其自身
让我们先来认识认识递归:
首先我们定义一个数列an=2 * n
那么an的前几项为2,4,6,8,10,12…
如果使用递归的思想,那么an=2 + an-1且a1=2,an的所有项并都没有改变,但是获取an的方法改变了,这就是递归的意义
定义an的代码可以这样定义:
int getAn(int n){
int an = 2 * n;
return an;
}
那么使用递归就是:
int getAn(int n){
int an = 2 + getAn(n - 1);
return an;
}
编写时不会报错,但是当我们开始运行,瞬间报错了,错误原因是
Exception in thread “main” java.lang.StackOverflowError
即栈内存溢出了,因为这个程序一旦开始运行就停不下来了,内存当然会溢出
所以我们应该给他额外写一个判断,正如之前定义an时要补充a1=2
int getAn(int n){
int an;
if(n == 1 )
an = 2;
else
an = 2 + getAn(n - 1);
return an;
}
当然你可能会发现,递归不仅逻辑复杂,而且对于内存的使用更是远大于循环,所以其实递归也是一个被抛弃的设定(所以跟变参方法放在一起讲了)。在平常的项目中能使用循环的一定要用循环,不到万不得已,不推荐使用递归