Practice| 面向对象
实参与形参的传递机制
* 实参给形参赋值:
* 1、基本数据类型:
* 实参给形参的数据值,形参的修改和实参无关
* 2、引用数据类型
* 实参给形参的地址值,如果这个地址值修改“属性”会影响实参,但是你如果修改是地址值,和实参无关了
*/
class MianShiTi{ String str = new String("good idear"); //创建字符串对象; char[] ch = {'a','b','c','d'}; //创建数组; public static void main(String[] args){ MianShiTi m = new MianShiTi(); //创建一个对象; exchange(m.str, m.ch); //m.str没有变,m.ch的[1]变了 System.out.println(m.str); //good idear ,它们是两个无关的str System.out.println(m.ch); //a*cd String s = "bbbb"; StringBuffer b = new StringBuffer("bbbb"); change(s,b); //s不可变,s仍然"bbbb" //b的内容就从"bbbb" ---> "aaaa" System.out.println(s+b); //bbbbaaaa } public static void exchange(String str, char[] ch){ //两个都是引用数据类型String和char[]数组, str = "Hello World"; //字符串对象是不可变的,一旦修改str=xxx,就是产生新的对象,和原来的"实参"无关 System.out.println(str); //Hello World ch[1] = '*'; //数组元素是可变的,ch形参和上面的m.ch实参指向的是同一个数组,所以ch[1]修改,就是修改m.ch[1] System.out.println(ch); //a*cd } private static void change(String s, StringBuffer b){ //两个形参为String类型和 类 都是引用数据类型 s = "aaa"; b.setLength(0);//b的长度设置为0,相当于清空原来b中内容 b.append("aaaa");//append表示追加,又追加了aaaa } }
public class MainTest{ public static void test(String str){ str = "hello"; } public static void main(String[] args){ String str = "beijing"; test(str); System.out.println(str); //beijing } }
public class TestMethod { //i,str,j是形参 public static void change(int i, String str, Integer j, MyData my){ i++;//这里i无论怎么改,和实参无关 str += "尚硅谷";//字符串对象是不可变的,一旦改变,会产生新对象,str中的地址值变了 j++;//Integer对象是不可变的,一旦改变,会产生新对象,j中的地址值变了 //my.num = 20; my = new MyData(); my.num = 100; } public static void main(String[] args){ int a = 10; String s = "hello"; Integer b = 20; MyData m = new MyData(); change(a,s,b,m);//a,s,b,m是实参 System.out.println("a = " + a); //a=10 System.out.println("s = " + s); //s=hello System.out.println("b = " + b); //b=20 System.out.println("m.num = " + m.num); //n.num=100 } } class MyData{ int num = 10; }
class TEXT{ public int num; public String str; public TEXT(int num, String str){ this.num = num; this.str = str; } } public class Class4 { public static void f1(TEXT tIn, int intIn, Integer integerIn, String strIn){ tIn.num =200; tIn.str = "bcd"; intIn = 200; integerIn = 200; strIn = "bcd"; } public static void main(String[] args) { TEXT tIn = new TEXT(100, "abc"); //tIn引用数据类型,对象 int intIn = 100; Integer integerIn = 100; String strIn = "abc"; f1(tIn,intIn,integerIn,strIn); //引用数据类型 + 引用数据类型+int+Integer +String System.out.println(tIn.num + tIn.str + intIn + integerIn + strIn); } //200 bcd + 100 + 100 + abc } ===>>>200bcd100100abc
用数组来管理属性、对象等
/* (1)声明一个日期类型MyDate:有属性:年year,月month,日day (2)创建2个日期对象,分别赋值为:你的出生日期,你对象的出生日期,放到数组中 (3)遍历显示 */
public class TestMydate { public static void main(String[] args) { MyDate[] arr = new MyDate[2]; //创建长度为2的数组 arr[0] = new MyDate(); //引用数据类型需要给它赋值一个对象; arr[0].year = 1994; arr[0].month = 7; arr[0].day = 7; arr[1] = new MyDate(); arr[1].year = 1996; arr[1].month = 7 ; arr[1].day = 7; for(MyDate num : arr){ System.out.println(num.year + "-" + num.month + "-" + num.day); }
class TestTeacher{ public static void main(String[] args){ //用数组来管理属性,创建的对象,排序等 Teacher[] arr = new Teacher[5]; //声明一个一维数组,来存储5个Teacher的对象; arr[0] = new Teacher(); //arr[0]代表一个元素,是Teacher类型(引用数据类型),需要赋值为一个Teacher对象; arr[0].name = "kris"; arr[0].age = 18; arr[0].gender = '男'; arr[0].course = "java"; Teacher t2 = new Teacher(); t2.name = "alex"; t2.age = 11; t2.gender = '女'; t2.course = "Mysql"; arr[1] = t2; for(int i = 0;i < arr.length;i++){ //arr[i]是一个Teacher的对象 //Exception in thread "main" java.lang.NullPointerException空指针异常 //一旦报NullPointerException,说明对象是null //arr[i]是否是null? //if(arr[i] != null){ System.out.println(arr[i].name + "\t" + arr[i].age + "\t" + arr[i].gender + "\t" +arr[i].course); //} } } } class Teacher{ String name; int age; char gender; String course; }
练习2 声明一个圆类Circle,有属性半径radius 声明一个测试类TestCircleArray, 在main方法中,创建一个Circle数组,长度为3,并创建三个Circle对象放到数组中, 遍历显示数组,显示每一个圆的半径、面积。 然后在对Circle数组进行排序,按照半径从小到大,再次遍历显示数组,显示每一个圆的半径、面积 */ class TestCircle2{ public static void main(String[] args){ Circle[] arr = new Circle[3]; //创建一个Circle数组,长度为3,默认值都为null //创建3个Circle对象放到数组中, 动态输入创建 java.util.Scanner input = new java.util.Scanner(System.in); for(int i = 0; i < arr.length;i++){ arr[i] = new Circle(); //先创建圆Circle的对象 System.out.print("请输入圆的半径:"); arr[i].radius = input.nextDouble(); System.out.println("半径" + arr[i].radius + "\t" + "面积" + Math.PI * arr[i].radius * arr[i].radius); } /* //静态输入 arr[0] = new Circle(); arr[1] = new Circle(); arr[2] = new Circle(); arr[0].radius = 2.0; arr[1].radius = 3.0; arr[2].radius = 1.0; */ //冒泡排序按从小到大顺序排,需要排arr.length-1次,大的就往后 //第一轮:i=1,j=0,1两次 //第二轮:i=2,j=1一次 for(int i = 1; i < arr.length;i++){ for(int j = 0;j < arr.length-i;j++){ //如果前面的圆的半径比较后面的圆的半径大,就交换两个圆 if(arr[j].radius > arr[j+1].radius){ //比较的是圆的半径,对象需要.半径属性 Circle temp = arr[j];//temp的类型和arr[j]和arr[j+1]一样,它俩是Circle arr[j] = arr[j+1]; arr[j+1] = temp; } } } for(int i = 0; i < arr.length;i++){ System.out.println(arr[i].radius); } } } class Circle{ double radius; }
练习3 (1)声明一个丈夫类型Husband,有属性:姓名,年龄,妻子,其中妻子是Wife类型 (2)声明一个妻子类型Wife,有属性:姓名,年龄,丈夫,其中丈夫是Husband类型 (3)创建一个丈夫对象,创建一个妻子对象,并显示他们的信息 */ class Test3{ public static void main(String[] args){ Husband hus = new Husband(); //hus丈夫对象; hus.name = "kris"; hus.age = 20; Wife wif = new Wife(); //wif妻子对象; wif.name = "xka"; wif.age = 19; hus.qi = wif; //qi为引用数据类型,必须给它赋值一个对象; wif.fu = hus; System.out.println("丈夫的信息:"+hus.name + "\t" + hus.age + "\t" + "妻子的信息:" + hus.qi.name + hus.qi.age); System.out.println("妻子的信息:" + wif.name + "\t" + wif.age + "\t" +"丈夫的信息:"+ wif.fu.name); } } class Husband{ String name; int age; Wife qi; } class Wife{ String name; int age; Husband fu; }
/* 2、练习2 (1)声明一个日期类型MyDate,包含年、月、日 (2)声明一个员工类,包含属性:姓名、薪资、生日,其中生日是MyDate类型 (3)声明一个测试类,在main方法中,声明一个数组,创建三个员工对象,并为属性赋值 (4)遍历显示员工信息,并且按照生日排序 (5)遍历显示员工信息,按照薪资排序 */ class Test2{ public static void main(String[] args){ Staff[] arr = new Staff[3]; //声明一个数组,创建员工对象,并为他们赋值; java.util.Scanner input = new java.util.Scanner(System.in); for(int i = 0; i < arr.length; i++){ arr[i] = new Staff(); System.out.print("请输入员工姓名:"); arr[i].name = input.next(); System.out.print("员工薪资:"); arr[i].salary = input.nextInt(); System.out.println("请输入员工生日"); arr[i].birthday = new MyDate(); //birthday为引用数据类型,必须给它赋值一个对象; System.out.print("年:"); arr[i].birthday.year = input.nextInt(); System.out.print("月:"); arr[i].birthday.month = input.nextInt(); System.out.print("日:"); arr[i].birthday.day = input.nextInt(); } System.out.println("姓名\t" + "薪资\t" + "生\t日"); //遍历显示员工信息;foreach循环 //for(元素的数据类型 元素名 : 数组名){} for(Staff num : arr){ //System.out.println(arr[i].name + "\t" + arr[i].salary + "\t" + arr[i].birthday.year + "-" + arr[i].birthday.month + "-" + arr[i].birthday.day); System.out.println(num.name + "\t" + num.salary + "\t" + num.birthday.year + "-" + num.birthday.month + "-" + num.birthday.day); } //冒泡排序;按员工生日排序;从小到大 for (int j = 1;j < arr.length;j++){ for(int i = 0;i < arr.length-i; i++){ if(arr[i].birthday.year > arr[i+1].birthday.year){ //先比较年year,大的年龄小 Staff temp = arr[i]; arr[i] = arr[i+1]; arr[i+1] = temp; }else if(arr[i].birthday.year == arr[i+1].birthday.year){ if(arr[i].birthday.month > arr[i+1].birthday.month){ Staff temp = arr[i]; arr[i] = arr[i+1]; arr[i+1] = temp; }else if(arr[i].birthday.month == arr[i+1].birthday.month){ if(arr[i].birthday.day > arr[i+1].birthday.day){ Staff temp = arr[i]; arr[i] = arr[i+1]; arr[i+1] = temp; } } } } } //遍历排序之后的 System.out.println("排序之后的:"); System.out.println("姓名\t" + "薪资\t" + "生\t日"); for(int i = 0;i < arr.length; i++){ System.out.println(arr[i].name + "\t" + arr[i].salary + "\t" + arr[i].birthday.year + "-" + arr[i].birthday.month + "-" + arr[i].birthday.day); } } } class MyDate{ int year; int month; int day; } class Staff{ String name; double salary; MyDate birthday; }
类的继承,super 1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:账号id,余额balance,年利率annualInterestRate;
包含的方法:访问器方法(getter和setter方法),返回月利率的方法getMonthlyInterest(),取款方法withdraw(),存款方法deposit()。 写一个测试类TestAccount:在用户程序中,创建一个账号为11223344、余额为20000、年利率4.5%的Account对象。使用withdraw方法提款30000元,并打印余额。 再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。 2、创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额,。在CheckAccount类中重写withdraw方法。 写一个用户程序测试CheckAccount类。在用户程序中,创建一个账号为11223344、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。 提示: (1)子类CheckAccount的构造方法需要将从父类继承的3个属性和子类自己的属性全部初始化。 (2)父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw方法中需要修改它的值 最后思考:如果将父类Account的属性balance设置为protected,会怎么样?
package com.atguigu.variable; import com.atguigu.inherited.Account; public class CheckAccount extends Account { private double overdraft; public CheckAccount() { super(); } public CheckAccount(String id, double balance, double annualInterestRate, double overdraft) { super(id, balance, annualInterestRate); this.overdraft = overdraft; } public double getOverdraft() { return overdraft; } public void setOverdraft(double overdraft) { this.overdraft = overdraft; } /* public void withdraw(double money){ if(money < 0){ System.out.println("输入有误"); }if(money < getBalance()){ super.withdraw(money); //正常取,可以使用父类里边的方法withdraw }else{ //可以透支 if(money > getBalance() + overdraft){ System.out.println("超过可透支额度"); }else{ System.out.println("剩余可透支额度为:" + (overdraft -= money - getBalance())); //取后剩余的透支额度; //超过的额度(money - balance); setBalance(0); System.out.println("哎呀取完了,账户余额为:" + getBalance()); } } }*/ // 如果将父类Account的属性balance设置为protected,表在其他包的子类里边也可以使用 public void withdraw(double money){ if(money < 0){ System.out.println("您的输入有误"); }if(money < balance){ super.withdraw(money); }else{ if(money > balance + overdraft){ System.out.println("超过卡的取款额度"); }else{ System.out.println("可透支额度剩余:" + (overdraft -= money -balance)); setBalance(0); } } } }
package com.atguigu.variable; import com.atguigu.inherited.Account; public class TestAccount { public static void main(String[] args) { // TODO Auto-generated method stub Account a = new Account("11223344", 20000, 0.045); CheckAccount c = new CheckAccount("11223344", 20000, 0.056, 5000); c.withdraw(28000);//传参调用withdraw方法,它是没有返回值的。 //System.out.println(a.getBalance()); System.out.println(a.deposit(2000)); } }
多态转型
// A(show(D obj) show(A obj)) --> B(show(B obj) show(A obj) ) --> C // --> D //父类的变量可以存储子类的对象;但子类的变量不能存储父类的对象; D13Exam public class Exam1 { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); //向上转型 a2对象可以调用B中重写父类的方法show(A obj)(重写的形参看是否符合 父类的引用变量接收子类的对象。) 和 父类中的方法。 B b = new B(); //本态引用,b可以调用它自己即子类B中的方法和继承父类的方法,只要参数符合就可以。 C c = new C(); D d = new D(); System.out.println("(1)" + a1.show(b));//调用A类中的哪个show方法,实参是子类B的对象b,b可以传给它的父类引用变量 A obj 多态参数 System.out.println("(2)" + a2.show(b));//B类的对象有几个show方法? D继承了B,B继承了A;
//((1)show(D obj)(2)show(A obj)(3)show(B obj)),这里传的实参类型D类型d对象,选择show(D obj)执行
System.out.println("(3)" + b.show(c));//b类的对象有几个方法? b可以调用B和A中所有的方法,形参c 可以传给它的父类;多态参数。 // 这里传的实参类型是C类型的c对象,如果有形参C类型的,肯定首选,这里没有,看哪个合适,选择B类型 System.out.println("(4)" + b.show(d)); // 这里传的实参类型是D类型的d对象,有合适的, } } class A{ public String show(D obj){ return ("A and D"); } public String show(A obj){ //a1对象可以调用哪个传入B类型的show方法呢?B继承了A return "A and A"; } } class B extends A{ public String show(B obj){ return "B and B"; } @Override public String show(A obj){ //重写了父类show(A obj)的方法 return "B and A"; } } class C extends B{ } class D extends B{ ---》》 (1)A and A (2)B and A (3)B and B (4)A and D
多态针对方法的,属性没有多态,注意权限修饰符
public class Exam2 { public static void main(String[] args) { Base b = new Sub();//多态引用 System.out.println(b.x);//属性没有多态,按照编译时,b对象有1个x,是Base类中 //1 } } class Base{ int x = 1; } class Sub extends Base{ int x = 2; }
public class Child extends Father{ public String grade; public static void main(String[] args){ Father f = new Child(); //向上转型; System.out.println(f.name);//因为name是private的,所以编译错误; } } class Father{ private String name = "atguigu"; //private 只能在本类中使用。 int age = 0; }
this、继承时构造器的调用
public class Teacher extends Person{ private String name = "tom"; public Teacher(){ System.out.println("this is a teacher."); // super(); //编译错误,必须在子类构造器的首行 } public static void main(String[] args){ Teacher tea = new Teacher(); // System.out.println(this.name);//编译错误,this不能在static方法中使用的 } } class Person{ public Person(){ System.out.println("this is a Person."); } }
继承、构造器、super、方法的重写、多态引用
public class Test { public static void main(String[] args) { Base b1 = new Base();//本态引用,只看Base类,执行了Base类的无参构造,在无参构造中,调用了Base类的method(100) Base b2 = new Sub(); //执行的是子类重写的方法。 //多态引用,创建的是子类的对象,执行子类Sub的无参构造; //在子类的构造器中,一定会调用父类的构造器,默认调用的是父类的无参构造,会导致父类的无参构造被执行,因为父类的无参构造中调用method(100), //它省略了“this.”,这个this是当前对象,当前正在创建的是子类Sub的对象,执行的是子类Sub重写的method(100) //接着在子类的构造器中,有super.method(70),这个执行的是父类的method(70),所以会打印 Base:70 } } class Base{ Base(){ method(100); //省略了this.method(100),this指当前对象 } public void method(int i){ System.out.println("base : " + i); // 1.base: 100; 3. base: 70 } } class Sub extends Base{ Sub(){ //省略了super(); super.method(70); //super.方法。子类重写了method方法,但是它又想调用父类的method方法了。 } public void method(int j){ //子类重写了method方法。 System.out.println("sub : " + j); //2. sub: 100 ; } } --->> base : 100 sub : 100 base : 70
类初始化和实例初始化
如果没有实例化(例如main方法中执行了某个函数),没有创建对象,只执行类的实例化,不进行实例的初始化。
public class Test { { System.out.println("b"); } static{ System.out.println("a"); } Test(){ System.out.println("c"); } public static String getOut(){ try { return "1"; } catch (Exception e) { return "2"; }finally{ return "3"; } } public static void main(String[] args) { System.out.println(getOut()); } } ===》》 a 3
* 类初始化和实例初始化 * (1)先类初始化 * A:如果父类没有初始化,会先初始化父类 * B:类的初始化执行的是<clinit>方法,它由两部分组成: * (a)静态变量的显式赋值语句 (b)静态代码块的语句,(a)(b)谁在上谁先执行 * * (2)实例初始化 * A:创建子类对象时,也会导致父类的实例初始化方法执行 * B:每一个构造器都会有对应的实例初始化方法 * C:每一个实例初始化有三部分组成: * (a)非静态变量的显示初始化代码(b)非静态代码块的语句(c)构造器; (a)(b)谁在上谁先执行,构造器后执行
子类继承父类,子类和父类都有无参构造和有参构造;子类在进行创建对象时,不管子类创建对象时候它有没有传参,
子类都是默认调用父类的无参构造,如果传参了,那么参数传给的是子类的有参构造。
练习:
/* * 实例初始化的过程: * 1、父类的实例初始化<init>() * 因为子类默认调用父类的无参构造,那么选择父类的<init>(),而不是<init>(String name) * System.out.print("1"); * 2、子类的实例初始化 * System.out.print("3"); * father = new People(name +" F"); //创建了父类的对象 * 这句代码会导致,父类的有参构造,即父类的<init>(String name)会执行 * System.out.print("2"); * */ public class TestChild { public static void main(String[] args) { new Child("mike");// } } class People{ private String name; public People(){ System.out.print("1"); } public People(String name){ System.out.print("2"); this.name = name; System.out.println(name); } } class Child extends People{ People father; //子类的构造器中,默认会调用父类的无参构造 public Child(String name){ System.out.print("3"); father = new People(name +" F"); } public Child(){ System.out.print("4"); } } 打印:--->> 132mike F
父类初始化< clinit >---->>>子类初始化< clinit >--->> 父类的实例初始化< init > --->>子类的实例初始化< init >;一开始都是静态的先初始化。
(1.先父类初始化;2.子类初始化; 3.父类实例初始化; 4.子类实例初始化。)
练习:
* 1、实例初始化,为属性赋值的过程 * (1)父类的实例初始化方法<init>() * x = 10;//父类中的x * this.print();//要考虑方法的重写,因为在创建子类Son的对象,所以这个this是子类的对象 * System.out.println("Son.x = " + x);// Son.x = 0 ,这个是子类中的x * x = 20;//父类中的x * (2)子类的实例初始化方法<init>() * x = 30;//子类的x * this.print(); * System.out.println("Son.x = " + x);//子类中的x,就近原则 Son.x = 30 x = 40;//子类的x * * 2、属性没有多态 * System.out.println(f.x);//按照编译时类型,父类的x */ public class Exam3 { public static void main(String[] args) { Father f = new Son(); System.out.println(f.x);//先初始化最后执行这个,按照编译时类型,父类的x;属性没有多态。 } } class Father{ int x = 10; //父类的实例初始化; 显式赋值; public Father(){ //构造器; this.print();//this指的是当前对象,即Son对象,它重写了print方法,所以调用的是子类的print方法 x = 20; } public void print(){ System.out.println("Father.x = " + x); } } class Son extends Father{ int x = 30; public Son(){ this.print(); x = 40; } public void print(){ //执行这个方法的时候x还没赋值,它的默认值为0 System.out.println("Son.x = " + x);//子类中的x,就近原则 } }
----->>> 打印:
Son.x = 0
Son.x = 30
20
* 实例初始化的问题: * 1、创建父类对象时 * 执行父类的实例初始化方法<init> * System.out.println("father create"); * 2、创建子类对象时 * 执行父类的实例初始化方法<init> * System.out.println("father create"); * 执行子类的实例初始化方法<init> * System.out.println("child create"); */ public class Child extends Father{ public Child(){ System.out.println("child create"); } public static void main(String[] args) { Father f = new Father(); //先执行这一句,父类 f 的实例初始化 Child c = new Child(); //再执行子类的实例初始化 --> 它会导致父类的先初始化再初始化子类 } } public class Father { public Father(){ System.out.println("father create"); } } 打印: father create father create child create
* 1、创建对象 * 实例初始化 * (1)父类的实例初始化<init> * name = "father"; //这个name是父类的name * (2)子类的实例初始化<init> * name = "test"; //这个name是子类的name * * 2、对象调用方法 * 调用的是子类从父类继承的getName() * * 子类的对象有两个name, this.name,如果在子类中,this.name就是子类的,如果在父类中,this.name就是父类的 */ public class Test extends Father{ private String name = "test"; public static void main(String[] args) { Test test = new Test(); System.out.println(test.getName()); } } public class Father { private String name = "father"; public String getName() { return name;//这里就近原则,找的是父类的name } }
* 1、创建对象 * Other o = new Other(); o.i = 0; * 2、调用了addOne(o) * o.i++; o.i = 1; * 3、final修饰的变量不可变问题 * 是修饰什么变量,局部变量还是成员变量? */ public class Something { public static void main(String[] args) { Other o = new Other(); new Something().addOne(o); //对象.方法 System.out.println(o.i); } //final修饰的变量不可变; 这里是o变量不可变 public void addOne(final Other o){ // o = new Other();//o的变量值不可变 o.i++; } } class Other{ public int i; }
* 1、static * 本类中,静态的方法,代码块可以访问静态的属性 * 2、类初始化 * main方法所在的类必须先初始化,然后才能执行main方法 * * Myclass2类的初始化<clinit>() * static{ int x = 5; x--;//这里的x是局部变量的x,不是类变量的x x = 4 } static{ x--;//这里的x是类变量的x x = -1 } * 在执行main * System.out.println("x = " + x);//类变量的x x=-1 z--;//类变量的z z= -1 myMethod(); y = z++ + ++z;//都是类变量 (1)先把z=-1的值load到操作数栈(2)z++,z=0(3)++z,z=1(4)再把z=1的值load到操作数栈(5)计算-1+1(6)赋值给y=0 System.out.println("result: " + (z + y + ++z)); (1)先把z=1的值load到操作数栈(2)再把y=0的值load到操作数栈 (3)++z,z=2(4)先把z=2的值load到操作数栈(5)执行1+0+2(6)打印3 * 2、局部变量与成员变量(类变量、实例变量) * 3、自增问题 * */ public class Myclass2 { static int x,y,z; static{ int x = 5; //局部变量 x--; } static{ x--; //这里的x是类变量 } public static void main(String[] args) { System.out.println("x = " + x);//类变量的x x=-1 z--;//类变量的z z= -1 myMethod(); System.out.println("result: " + (z + y + ++z)); } public static void myMethod(){ y = z++ + ++z; } } x = -1 result: 3
==和equals()的区别 ==:如果是基本数据类型,比较的是数据值 如果是引用数据类型,比较的是对象的地址值 equals():必须是对象才能调用,它是Object类中声明的,如果子类没有重写,那么它的效果和==是一样的,也是比较对象的地址。如果子类重写了,
那么就按照重写的规则进行比较,例如String类型重写了equals,比较的是字符串的字符内容。
重写一个类的equals方法需要主意什么?
(1)必须和hashCode()方法一起重写,凡是参与equals比较的属性,一定要参与hashCode值的计算。 (2)equals的重写要求遵循几个原则: equals 方法在非空对象引用上实现相等关系: • 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。 • 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。 • 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。 • 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。 • 对于任何非空引用值 x,x.equals(null) 都应返回 false。