8.1 Java异常
1、什么是异常?
异常:(字面意思),不正常。
不是异常:(1)编译错误,语法错误,无法允许
(2)逻辑错误,语法正确,永远得不到正确的结果
大部分异常:
大部分时候可以正常运行,只是某些情况会无法运行。
某些情况:A:用户的非法输入
B:网络中断
C:磁盘空间已满
....
特殊异常:
违反了程序的逻辑规定,例如:下标越界,空指针异常,算术异常等(尽量避免)
2、Java如何表示异常?
Java中一切皆对象。同样,异常也是用某些类型的对象来表示。
例如:下标越界异常 ==> ArrayIndexOutOfBoundsException
空指针异常 ==> NullPointerException
算术异常 ==> ArithmeticException
类型转换异常 ==> ClassCastException
....
import java.util.Scanner;
public class TestException {
public static void main(String[] args) {
/* int a;
System.out.println(a);*/
// System.out.println(add(1,1));
Scanner input = new Scanner(System.in);
/* System.out.print("请输入一个整数:");
int num = input.nextInt();
System.out.println("num = " + num);
input.close();*/
// int[] arr = {1,2,3,4,5};
// System.out.println(arr[5]);
//
// System.out.println(1/0);
//FileInputStream fis = new FileInputStream("d:\1.txt");//表示我要读取d:\1.txt文件的 内容
}
public static int add(int a, int b){
return a - b;
}
}
8.2 异常体系结构
3、异常的类型体系结构
(1)根类型(作为异常和错误的根类型):java.lang.Throwable
类的根仍然是Object。
Throwable 类是 Java 语言中所有错误或异常的超类。
只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句“抛”出。
类似地,只有此类或其子类之一才可以是 catch (“捕获”)子句中的参数类型。
两个子类的实例,Error 和 Exception,通常用于指示发生了异常情况。
(2)Error:用于指示合理的应用程序不应该试图捕获的严重问题。
例如:VirtualMachineError(虚拟机错误)
它的子类:StackOverflowError(栈内存溢出,在无限循环递归见过)
OutOfMemoryError(堆内存溢出)
(3)Exception:指出了合理的应用程序想要捕获的条件。
Exception又可以分为两大类:
A:运行时异常:RuntimeException及其子类,建议能靠程序员的检查,代码的判断等避免,尽量避免。
例如:
下标越界异常 ==> ArrayIndexOutOfBoundsException
空指针异常 ==> NullPointerException
算术异常 ==> ArithmeticException
类型转换异常 ==> ClassCastException
编译器不会提前”检查或提示“你的代码会发生xx运行时异常。无论你这个异常是否真的会发生。
B:编译时异常:Exception下面除了 运行时异常,其余都是编译时异常。
编译器 会 明确“提示”你的代码“可能”发生xx编译时异常,哪怕你的代码并不会发生这个异常。
而且要求你“必须”提前编写好异常处理的代码,否则编译器不会放过你的,编译不通过。
比喻:
消防部分要求所有的公司都要准备消防器材。没有准备这些,一定不让开业。
例如:FileNotFoundException:文件找不到异常
IOException
SQLException
....
8.3 异常的抛出和处理机制
4、Java中的异常抛出和处理机制
当程序发生异常时,JVM会创建一个对应类型的异常对象,并且“抛”出,
如果该语句的外围有try..catch处理,可以“捕获”该异常,那么程序可以继续,
否则该异常对象就会被抛给上级,直到main,导致程序终止。
工具类
public class ArrayTools {
// 对给定的数组通过给定的角标获取元素。
public static int getElement(int[] arr, int index) {
int element = arr[index];
return element;
}
}
测试类
public class ExceptionDemo {
public static void main(String[] args) {
int[] arr = { 34, 12, 67 };
int num = ArrayTools.getElement(arr, 4);
System.out.println("num=" + num);
System.out.println("over");
}
}
上述程序执行过程图解:
8.5 try-catch处理
5、异常的处理关键字
(1)try-catch
try{
编写可能发生异常的业务代码
}catch(异常的类型1 异常对象名){ //异常对象名习惯上写e
捕获到“异常的类型1”的异常对象后,要如何处理的代码。
例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}catch(异常的类型2 异常对象名){ //异常对象名习惯上写e
捕获到“异常的类型2”的异常对象后,要如何处理的代码。
例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}
多个catch分支,从上往下判断,如果上面的匹配了,下面的就不看了。
如果都没有匹配,就相当于没有编写try-catch,即异常没有被处理。
JVM会自动抛给上级,如果一直到main方法都没有处理,就挂了。
快捷键:选中要加try-catch的代码,按Ctrl + Alt + T
(2)JDK1.7新特性
try{
编写可能发生异常的业务代码
}catch(异常的类型1 | 异常的类型2 异常对象名){ //异常对象名习惯上写e
捕获到“异常的类型1,2”的异常对象后,要如何处理的代码。
例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}catch(异常的类型3 异常对象名){ //异常对象名习惯上写e
捕获到“异常的类型3”的异常对象后,要如何处理的代码。
例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestTryCatch1 {
public static void main(String[] args) {
//从键盘输入一个正整数
Scanner input = new Scanner(System.in);
int num;
while(true) {
try {
System.out.print("请输入一个正整数:");
num = input.nextInt();
if (num <= 0) {
System.out.println("请重新输入,要求是正整数!");
}else{
break;
}
} catch (InputMismatchException e) {
System.out.println("请重新输入,要求是整数");
input.nextLine();
// String str= input.nextLine();//清理上次错误的输入数据
// System.out.println("错误数据:" + str);
}
}
System.out.println("num = " + num);
input.close();
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestTryCatch2 {
public static void main(String[] args) {
//从键盘输入一个正整数
Scanner input = new Scanner(System.in);
int num = 0;
try {
while (true) {
System.out.print("请输入一个正整数:");
num = input.nextInt();
if (num <= 0) {
System.out.println("请重新输入,要求是正整数!");
} else {
break;
}
}
} catch (InputMismatchException e) {
System.out.println("请重新输入,要求是整数");//无法重新输入,因为catch完就执行下面的代码
input.nextLine();
}
System.out.println("num = " + num);
input.close();
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestTryCatch3 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int bei;
while(true) {
try {
System.out.print("请输入被除数:");
bei = input.nextInt();
break;
} catch (Exception e) {
input.nextLine();
System.out.println("刚才输入的不是整数");
}
}
int chu;
while(true) {
try {
System.out.print("请输入除数:");
chu = input.nextInt();
System.out.println("商:" + bei/chu);
break;
} catch (InputMismatchException e) {
input.nextLine();
System.out.println("刚才输入的不是整数");
}catch(ArithmeticException e){ //多个catch
System.out.println("除数不能为0");
}
}
input.close();
}
}
8.6 异常信息的打印
6、异常的输出
(1)System.out.println(xx); ==》 普通信息输出
(2)System.err.println(xx); ==> 错误信息输出
(3)e.printStackTrace(); ==> 标准的异常信息输出
输出异常的详细信息,包括异常的类型、异常的message说明信息、异常的堆栈跟踪信息。
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestTryCatch4 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int bei;
while(true) {
try {
System.out.print("请输入被除数:");
bei = input.nextInt();
break;
} catch (Exception e) {
input.nextLine();
// System.out.println("刚才输入的不是整数");
System.err.println("刚才输入的不是整数");
}
}
int chu;
while(true) {
try {
System.out.print("请输入除数:");
chu = input.nextInt();
System.out.println("商:" + bei/chu);
break;
} catch (InputMismatchException e) {
input.nextLine();
// System.out.println("刚才输入的不是整数");
System.err.println("刚才输入的不是整数");
}catch(ArithmeticException e){
// System.out.println("除数不能为0");
System.err.println("除数不能为0");
}
}
input.close();
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestTryCatch5 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int bei;
while(true) {
try {
System.out.print("请输入被除数:");
bei = input.nextInt();
break;
} catch (Exception e) {
input.nextLine();
// System.out.println("刚才输入的不是整数");
// System.err.println("刚才输入的不是整数");
e.printStackTrace();
}
}
int chu;
while(true) {
try {
System.out.print("请输入除数:");
chu = input.nextInt();
System.out.println("商:" + bei/chu);
break;
} catch (InputMismatchException e) {
input.nextLine();
// System.out.println("刚才输入的不是整数");
// System.err.println("刚才输入的不是整数");
e.printStackTrace();
}catch(ArithmeticException e){
// System.out.println("除数不能为0");
// System.err.println("除数不能为0");
e.printStackTrace();
}
}
input.close();
}
}
8.7 finally
7、finally关键字
(1)try-catch-finally
try{
编写可能发生异常的业务代码
}catch(异常的类型1 异常对象名){ //异常对象名习惯上写e
捕获到“异常的类型1”的异常对象后,要如何处理的代码。
例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}catch(异常的类型2 异常对象名){ //异常对象名习惯上写e
捕获到“异常的类型2”的异常对象后,要如何处理的代码。
例如:打印异常信息也是一种处理,什么都不写也是一种处理。
}finally{
必须执行的代码。
无论try{}是否发生异常,也不管catch是否能够捕获异常,或者有没有catch都无所谓,
必须执行finally里面的代码。
甚至try{},catch{}有return语句,都阻挡不了finally的执行。
通常这里面编写的是资源释放的代码。例如:IO流的关闭、数据库链接的断开、网络断开等代码。
}
(2)try-finally
try{
编写可能发生异常的业务代码
}finally{
}
public class TestFinally {
public static void main(String[] args) {
/* try{
int a = 1;
int b = 0;
System.out.println(a/b);
}catch(ArithmeticException e){
// e.printStackTrace();
System.out.println("除数不能为0");
}finally{
System.out.println("finally");
}*/
/* try{
int a = 1;
int b = 1;
System.out.println(a/b);
}catch(ArithmeticException e){
e.printStackTrace();
}finally{
System.out.println("finally");
}*/
/* try{
int a = 1;
int b = 0;
System.out.println(a/b);
}catch(NullPointerException e){
// e.printStackTrace();
System.out.println("空指针异常");
}finally{
System.out.println("finally");
}*/
/* try{
int a = 1;
int b = 0;
System.out.println(a/b);
return;//结束当前方法,这里结束main
}catch(ArithmeticException e){
e.printStackTrace();
return;
}finally{
System.out.println("finally");
}*/
/*try{
int a = 1;
int b =1 ;
System.out.println(a/b);
System.exit(0);//退出JVM 了解
}catch(ArithmeticException e){
e.printStackTrace();
}finally{
System.out.println("finally");
}*/
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestFinally2 {
public static void main(String[] args) {
//从键盘输入一个正整数
Scanner input = new Scanner(System.in);
int num;
while(true) {
try {
System.out.print("请输入一个正整数:");
num = input.nextInt();
if (num <= 0) {
System.out.println("请重新输入,要求是正整数!");
}else{
break;
}
} catch (InputMismatchException e) {
System.out.println("请重新输入,要求是整数");
input.nextLine();
// String str= input.nextLine();//清理上次错误的输入数据
// System.out.println("错误数据:" + str);
}/*finally{
input.close();//暂时还不能关闭
}*/
}
System.out.println("num = " + num);
input.close();
}
}
8.8 throws
8、throws关键字
用于在方法签名(或者称为方法头)中表示当前方法“可能”发生xx类型异常,当前方法并未处理,需要调用者注意和处理的。
【修饰符】 返回值类型 方法名(【形参列表】)throws 异常类型列表{
方法体代码
}
理论上,throws后面所有可能发生的异常类型都可以列出来的。
但是通常情况下,一般只列出编译时类型异常,或非常重要的,需要特别提醒调用者的运行时异常类型。
例如:java.lang.Object类,
protected Object clone() throws CloneNotSupportedException
如果对象的类不支持 Cloneable 接口,则重写 clone 方法的子类也会抛出此异常,以指示无法复制某个实例。
重写:父子类中(包含父接口与实现类)
方法名相同
形参列表也相同(形参的类型、个数、顺序)
返回值类型(<=),(基本数据类型和void是必须相同,引用数据类型<=)
权限修饰符(>=)(但是要求父类被重写的方法在子类中是可见的,即不能是private,跨包不能是缺省),
其他修饰符(静态方法,final方法等不允许被重写),
throws 异常类型列表:
如果父类被重写方法的方法签名后面没有 “throws 编译时异常类型”,那么重写方法时,方法签名后面也不能出现“throws 编译时异常类型”。
如果父类被重写方法后面有throws 编译时异常类型列表,重写时①不加throws 编译时异常类型(异常处理了)
②继续throws 编译时异常类型,但是要求该异常类型要<=父类throws后面的编译时异常类型
对于throws后面的运行时异常类型不做任何要求。
public class Rectangle implements Cloneable{
private double length;
private double width;
public Rectangle() {
}
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
@Override
public String toString() {
return "Rectangle{" +
"length=" + length +
", width=" + width +
'}';
}
@Override
protected Rectangle clone() throws CloneNotSupportedException {
return (Rectangle) super.clone();
}
}
public class TestRectangle {
public static void main(String[] args) {
Rectangle r = new Rectangle(5,3);
System.out.println(r);
//需求:根据r对象“复制一个,克隆一个”同样长和宽的矩形对象。
/*
java.lang.Object类中有protected Object clone() throws CloneNotSupportedException
按理说Object类有这个方法,所有子类都应该有。
但是因为该方法的权限修饰符(访问控制修饰符)是protected,
可见性范围:
本类:Object本类
本包:java.lang包
其他包子类中:
如果是Rectangle这个子类的对象要调用,必须是Rectangle这个子类中。
而此时是在TestRectangle类中。
解决方案:Rectangle类必须重写Object类的clone方法。
*/
try {
Rectangle other = r.clone();
System.out.println(other);
} catch (CloneNotSupportedException e) {
System.out.println("无法完成克隆");
}
System.out.println("下面的代码");
}
}
8.9 throw
9、throw关键字
throw用于 在成员方法的方法体、构造器的方法体、代码块(后面反射再学)中用于“手动”抛出一个异常对象。
通常情况下,异常对象都是由JVM帮我们new,帮我们抛出的,但是有时候我们也可以“自己”new异常对象,自己抛异常对象。
语法格式:
throw new 异常类型(【实参列表】);
如果在throw语句的外围没有try..catch,那么throw语句会代替return语句结束当前方法,带回异常对象。
public class TestThrow {
public static void main(String[] args) {
System.out.println(max(4,2,3,1,6,3));
try {
System.out.println(max());
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(max(null));
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(max(new int[]{}));
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(max(new int[0]));
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(max(1,2));
} catch (Exception e) {
e.printStackTrace();
}
}
public static int max(int... nums){
if(nums == null || nums.length == 0){
throw new IllegalArgumentException("nums不代表一组int整数");
}
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}
}
8.10 自定义异常
10、自定义异常类型
要求:自定义异常类型必须继承Throwable或者其子类,通常都是继承Exception或RuntimeException.
如果是继承Exception类型,那么你的自定义异常类型是编译时异常类型。
如果是继承RuntimeException类型,那么你的自定义异常类型是运行时异常类型。
建议:自定义异常类型中包含几个构造器:无参构造,有参构造(给message赋值的构造器)
注意:自定义异常类型的对象只能程序员自己new,自己throw。
public class NotATriangleException extends Exception{
public NotATriangleException() {
}
public NotATriangleException(String message) {
super(message);
}
}
public class TriangleBaseNotNegativeException extends Exception{
public TriangleBaseNotNegativeException() {
}
public TriangleBaseNotNegativeException(String message) {
super(message);
}
}
public class Triangle {
private final double a;
private final double b;
private final double c;
public Triangle(double a, double b, double c) throws TriangleBaseNotNegativeException,NotATriangleException {
if(a < 0 || b < 0 || c <0){
// System.out.println("三角形边长不能为负数");
throw new TriangleBaseNotNegativeException("三角形边长不能为负数");
}
if(a+b<=c || b+c<=a || a+c<=b){
throw new NotATriangleException("三角形边长必须满足任意两边之和大于第三边");
}
this.a = a;
this.b = b;
this.c = c;
}
public double getA() {
return a;
}
public double getB() {
return b;
}
public double getC() {
return c;
}
@Override
public String toString() {
return "Triangle{" +
"a=" + a +
", b=" + b +
", c=" + c +
'}';
}
}
/*
表示三角形的边长不能为负数,以及三角形的边长没有满足任意两边之和大于第三边的要求
*/
public class TestDefineException {
public static void main(String[] args) {
try {
Triangle t = new Triangle(3,4,5);
System.out.println(t);
} catch (TriangleBaseNotNegativeException e) {
e.printStackTrace();
} catch (NotATriangleException e) {
e.printStackTrace();
}
try {
Triangle t = new Triangle(-3,4,5);
System.out.println(t);
} catch (TriangleBaseNotNegativeException e) {
e.printStackTrace();
} catch (NotATriangleException e) {
e.printStackTrace();
}
try {
Triangle t = new Triangle(1,1,5);
System.out.println(t);
} catch (TriangleBaseNotNegativeException e) {
e.printStackTrace();
} catch (NotATriangleException e) {
e.printStackTrace();
}
}
}
9.1 包装类
9.1.1 认识包装类
1、什么是包装类?
基本数据类型->包装类
byte <-> Byte
short <-> Short
int <-> Integer
long <-> Long
float <-> Float
double <-> Double
char <-> Character
boolean <-> Boolean
2、为什么要用包装类?
Java是面向对象的编程语言,很多的语法和API的设计都是面向“对象”设计的,
或者是针对引用数据类型设计的,不支持基本数据类型,
例如:
泛型、集合等都不支持基本数据类型。
Java不是“纯”面向对象语言。Java设计时保留了C/C++语言的8种基本数据类型,
以及针对这些基本数据类型而设计的丰富的运算符。
另外,因为基本数据类型比较简单,简洁,在不同的平台下面8种基本数据类型占用内存的宽度,
运算的规则都完全相同。
9.1.2 装箱与拆箱
3、那么基本数据类型与包装类之间如何转换呢?
在JDK1.5之前,是非常麻烦的,必须手动转换。
装箱:把基本数据类型的值包装为“对象”
拆箱:把“对象”拆解为基本数据类型的值
在JDK1.5之后,可以自动装箱与拆箱。
注意:自动装箱与拆箱只能支持对应类型之间。
import org.junit.Test;
import java.util.ArrayList;
public class TestWrapper {
@Test
public void test1(){
//手动装箱
int num = 1;
Integer obj = new Integer(num);//手动装箱
ArrayList list = new ArrayList();//它是一个集合,是一个容器,装对象的容器
// list.add(obj);
list.add(num);
}
@Test
public void test2(){
Integer obj1 = new Integer(100);
Integer obj2 = new Integer(200);
int sum = obj1.intValue() + obj2.intValue();//手动拆箱
System.out.println("sum = " + sum);
}
@Test
public void test3(){
//自动装箱
int num = 1;
Integer obj = num;
}
@Test
public void test4(){
//自动拆箱
Integer obj1 = new Integer(100);
Integer obj2 = new Integer(200);
int sum = obj1 + obj2;
System.out.println("sum = " + sum);
}
@Test
public void test5(){
int num = 1;
// Double d = num;//报错,int和Double不是对应类型
Double d2 = 1d;//数字后面加D,表示是double类型
}
}
9.1.3 API
4、包装类的一些好用的API
(1)基本数据类型与字符串之间的转换API
A:基本数据类型 -> 字符串 ,通常用的是拼接一个字符串,例如:""
B:字符串 -> 基本数据类型
包装类.parseXxx(字符串)
例如:
Integer.parseInt(字符串)
Double.parseDouble(字符串)
(2)数据类型的最大最小值
包装类.MAX_VALUE
包装类.MIN_VALUE
(3)字符转大小写
A:之前
大写->小写 +32
小写->大写 -32
B:现在
Character.toUpperCase(小写):转大写
Character.toLowerCase(大写):转小写
(4)十进制转二进制、八进制、十六进制(了解)
import org.junit.Test;
public class TestAPI {
@Test
public void test1(){
int num = 1;
String str = num + "";//基本数据类型 --> String
}
@Test
public void test2(){
String str1 = "123";
String str2 = "456";
int num1 = Integer.parseInt(str1);
int num2 = Integer.parseInt(str2);
int sum = num1 + num2;
System.out.println("sum = " + sum);
}
@Test
public void test3(){
String str1 = "12.3";
String str2 = "45.6";
double num1 = Double.parseDouble(str1);
double num2 = Double.parseDouble(str2);
double sum = num1 + num2;
System.out.println("sum = " + sum);
}
@Test
public void test4(){
System.out.println(Byte.MAX_VALUE);
System.out.println(Byte.MIN_VALUE);
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
}
@Test
public void test5(){
char letter = 'a';
char bigLetter = (char)(letter - 32 );
System.out.println("letter = " + letter);
System.out.println("bigLetter = " + bigLetter);
}
@Test
public void test6(){
char letter = 'a';
char bigLetter = Character.toUpperCase(letter);
System.out.println("letter = " + letter);
System.out.println("bigLetter = " + bigLetter);
}
@Test
public void test7(){
char letter = 'A';
char bigLetter = (char)(letter - 32 );
System.out.println("letter = " + letter);
System.out.println("bigLetter = " + bigLetter);
}
@Test
public void test8(){
char letter = 'A';
char bigLetter = Character.toUpperCase(letter);
System.out.println("letter = " + letter);
System.out.println("bigLetter = " + bigLetter);
}
@Test
public void test9(){
int num = 250;
System.out.println("二进制:" + Integer.toBinaryString(num));//11111010
System.out.println("八进制:" + Integer.toOctalString(num));//372
System.out.println("十六进制:" + Integer.toHexString(num));//fa
}
}
9.1.4 包装类对象的特点
5、包装类的特点
(1)部分包装类对象可以被缓存,缓存的常量对象就会被共享
Byte,Short,Integer,Long:会缓存部分对象 -128 ~ 127
Float,Double:不会缓存任何对象
Character:会缓存 0-127 范围的字符
Boolean:会缓存true,false
哪些情况会使用缓存对象?
A:在缓存范围内
B:自动装箱 或 用包装类的valueOf()方法得到的包装类对象
(2)包装类对象不可变(面试题经常考)
当包装类对象的值修改时,其实是指向了新对象。
(3)包装类对象计算时,
除了两个包装类对象 ==和!=,其余的都是自动拆箱计算的。
import org.junit.Test;
public class TestSpecial {
@Test
public void test1(){
Integer i1 = 1;//Integer i1 = new Integer(1);
Integer i2 = 1;//Integer i2 = i1;
System.out.println(i1 == i2);//true
}
@Test
public void test2(){
Integer i1 = 200;//Integer i1 = new Integer(200);
Integer i2 = 200;//Integer i2 = new Integer(200);
System.out.println(i1 == i2);//比较地址值 false
}
@Test
public void test3(){
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1 == i2);//false
}
@Test
public void test4(){
Integer i1 = Integer.valueOf(1);
Integer i2 = Integer.valueOf(1);
System.out.println(i1 == i2);//true
}
}
import org.junit.Test;
public class TestSpecial2 {
@Test
public void test1(){
int x = 1;
Integer y = 1;
Data data = new Data();
System.out.println("调用之前:" + x +"," + y +",data.num " + data.num);//1,1,0
change(x,y,data);
System.out.println("调用之后:" + x +"," + y +",data.num " + data.num);//1,1,1
}
/*
形参是基本数据类型,实参给形参的是数据值的副本,形参的修改和实参无关。
形参是引用数据类型,实参给形参的是地址值的副本,正常来说,形参修改了对象的值,相当于实参自己修改了对象的值。
*/
public void change(int a, Integer b, Data c){
Integer j = 1;
System.out.println(b == j);//true 比较地址值
a++;
b++;//等价于 b = Integer.valueOf(b) b指向了新对象
Integer i = 2;
System.out.println(b == i);//true 比较地址值
c.num++;
}
}
class Data{
int num;
}
import org.junit.Test;
public class TestSpecial3 {
@Test
public void test1(){
Integer i = 1;
Double d = 1.0;
//System.out.println(i == d);//无法比较,因为它们是比较地址值,比较地址值时,只能相同类型或父子类类型才能比较地址值
}
@Test
public void test2(){
Integer i = 1;
double d = 1.0;
System.out.println(i == d);//包装类与基本数据类型,会拆箱,把Integer拆箱为int,然后int自动升级为double
}
@Test
public void test3(){
Integer i = 1;
Integer j = 1;
System.out.println(i + j);//包装类与包装类的其他计算,都拆箱为基本数据类型 int + int
}
@Test
public void test4(){
Integer i = 1;
Double j = 1.0;
System.out.println(i + j);//包装类与包装类的其他计算,都拆箱为基本数据类型 int + double
}
@Test
public void test5(){
Integer i = 1;
Double j = 1.0;
System.out.println(i > j);//包装类与包装类的其他计算,都拆箱为基本数据类型 int > double
}
}
9.1.5 equals和==
两个对象比较是否相等:
(1)==
比较的是两个“对象”的“首地址”,只有这“两个”对象是同一个对象时,才会返回true。
例如:
Integer i1 = 1;
Integer i2 = 1;
System.out.println(i1 == i2);//true,使用了同一个缓存对象
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);//true ,这里也使用共享的字符串常量对象
Integer i1 = 190;
Integer i2 = 190;
System.out.println(i1 == i2);//false,不是同一个对象
(2)boolean equals(Object obj)
如果某个类“没有”重写Object类的equals方法,那么equals方法和==一样比较对象的首地址。
如果某个类“重写”了Object类的equals方法,那么执行的一定是重写后的代码,一般重写都是比较内容。
重写equals的快捷键是:Alt + Insert,选择重写equals和hashCode方法。
由此可知,Integer,String它们都重写了equals方法。
结论:
以后只要是对象比较是否相等,不管它是否重写equals方法,都调用equals方法来比较。
import org.junit.Test;
import java.util.Scanner;
public class TestInteger {
@Test
public void test1(){
Integer i1 = 1;
Integer i2 = 1;
System.out.println(i1 == i2);//true,使用了同一个缓存对象
}
@Test
public void test2(){
Integer i1 = 190;
Integer i2 = 190;
System.out.println(i1 == i2);//false,不是同一个对象
System.out.println(i1.equals(i2));//true
}
@Test
public void test3(){
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);//true ,这里也使用共享的字符串常量对象
}
@Test
public void test4(){
String s1 = "hello";
Scanner input = new Scanner(System.in);
System.out.print("请输入一个字符串(hello):");
String s2 = input.next();
System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//true
input.close();
}
}
import java.util.Objects;
public class Student {
private int id;
private String name;
private int score;
public Student(int id, String name, int score) {
this.id = id;
this.name = name;
this.score = score;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + ''' +
", score=" + score +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (id != student.id) return false;
if (score != student.score) return false;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {//这个方法先忽略
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + score;
return result;
}
}
import org.junit.Test;
public class TestEquals {
@Test
public void test1(){
Student s1 = new Student(1,"张三",89);
Student s2 = new Student(1,"张三",89);
System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//false(没重写) true(重写)
}
}