java学习笔记6
按住shift键,选择开始的一位和最后结束的一位来全选
面向对象特征之二:继承性(inheritance)
面向对象特征之二:继承性 1.继承性的理解 > 生活上:财产的继承、颜值的继承 > 代码层面: > 自上而下:定义了一个类A,在定义另一个类B时,发现类B的功能与类A相似,考虑类B继承于类A > 自下而上:定义了类B,C,D等,发现B、C、D有类似的属性和方法,则可以考虑将相同的属性和方法进行抽取, 封装到类A中,让类B、C、D继承于类A,同时,B、C、D中的相似的功能就可以删除了。 2.继承性的好处 - 继承的出现减少了代码冗余,提高了代码的复用性。 - 继承的出现,更有利于功能的扩展。 - 继承的出现让类与类之间产生了'is-a'的关系,为多态的使用提供了前提。 - 继承描述事物之间的所属关系,这种关系是:'is-a’的关系。可见,父类更通用、更一般,子类更具体。 3.继承的格式: class A{ //属性、方法 } class B extends A { } 继承中的基本概念: 类A:父类、superclass、超类、基类 类B:子类、subClass、派生类 4.有了继承性以后: > 子类就获取到了父类中声明的所有的属性和方法。 > 但是,由于封装性的影响,可能子类不能直接调用父类中声明的属性或方法。 > 子类在继承父类以后,还可以扩展自己特有的功能(体现:增加特有的属性、方法) extends:延展,扩展,延伸 子类和父类的理解,要区别于集合和子集 >不要为了继承而继承。在继承之前,判断一下是否有is a的关系。 5.默认的父类: Java中声明的类,如果没有显式的声明其父类时,则默认继承于java.lang.0bject 6.补充说明: > Java是支持多层继承。 > 概念:直接父类、间接父类 > Java中的子父类的概念是相对的。 Java中一个父类可以声明多个子类。反之,一个子类只能有一个父类(Java的单继承性)Person public class Person { //属性 String name; private int age; //方法 public void eat(){ System.out.println("人吃饭"); } public void sleep(){ System.out.println("人睡觉"); } public void show(){ System.out.println("age = " + age); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } Student public class Student extends Person { //属性 // String name; // int age; String school; String Study; //方法 // public void eat(){ // System.out.println("人吃饭"); // } // public void sleep(){ // System.out.println("人睡觉"); // } public void study(){ System.out.println("学生学习"); } public void show1() { // System.out.println("age : " + age); //封装的属性不能被继承 System.out.println("age : " + getAge()); } } ExtendsTest public class ExtendsTest { public static void main(String[] args) { Person p1 = new Person(); p1.name = "Tom"; // p1.age = 10; //私有封装的属性不能被访问 p1.eat(); System.out.println(p1.toString()); Student s1 = new Student(); s1.name = "Jack"; // s1.age = 12;//私有封装的属性不能被访问 s1.eat(); // 超纲:获取s1所属类的父类 System.out.println(s1.getClass().getSuperclass()); //class com.atguigu03._extends.Person // 超纲:获取p1所属类的父类 System.out.println(p1.getClass().getSuperclass()); //class java.lang.Object } }
练习题:
案例: (1)定义一个ManKind类,包括 成员变量int sex和int salary; 方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0); 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。 (2)定义类Kids继承ManKind,并包括 成员变量int yearsOld; 方法printAge()打印yearsOld的值。 (3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问 其父类的成员变量及方法。ManKind public class ManKind { private int sex; int salary; public ManKind() { } public ManKind(int sex, int salary) { this.sex = sex; this.salary = salary; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public void manOrWoman(){ if(sex == 1){ System.out.println("Man"); }else if(sex == 0){ System.out.println("Women"); } } public void employeed(){ if(salary == 0){ System.out.println("no job"); }else if(salary != 0) { System.out.println("job"); } } } Kids public class Kids extends ManKind { private int yearsOld; public Kids() { } public Kids(int yearsOld) { this.yearsOld = yearsOld; } //通过这个构造器可以直接给kid赋值所有属性,而不用分别设置set... public Kids(int sex, int salary, int yearsOld) { this.yearsOld = yearsOld; setSex(sex); setSalary(salary); } public int getYearsOld() { return yearsOld; } public void setYearsOld(int yearsOld) { this.yearsOld = yearsOld; } public void printAge() { System.out.println("I am " + yearsOld + " years old."); } } KidsTest public class KidsTest { public static void main(String[] args) { Kids kid = new Kids(); kid.setSex(1); kid.setSalary(100); kid.setYearsOld(12); //来自于父类当中的方法 kid.employeed(); //job kid.manOrWoman(); //Man //Kids类自己声明的方法 kid.printAge(); //I am 12 years old. } }
方法的重写(override/overwrite)
总结和练习:
一、测武4种权限修饰 在com.atguigu04.override包下创建两个package:test1和test2,测试Java中提供的4种杈限修饰 实际开发中,各权限修饰的使用频率是怎样的? public、private是使用频率最高的! 二、方法的重写 (overwrite / override) 1,为什么需要方法的重写? 子类在继承父类以后,就获取了父类中声明的所有的方法。但是,父类中的方法可能不太适用于子类,换句话说,子类 需要对父类中继承过来的方法进行覆盖、覆写的操作。 举例(银行账户): class Account{//账户 double balance;//余额 //取钱 public void withdraw(double amt){ //判断balance余额是否够amt取钱的额度 } } class CheckAccount extends Account{//信用卡 double protectedBy; //透支额度 public void withdraw(double amt){ //判断balance余额是否够amt取钱的额度 //如果不够,还可以考虑从protectedBy额度里取 } } class AccountTest{ public static void main(string[] args){ CheckAccount acct=new CheckAccount(); acct.withdraw(); //执行的是子类重写父类的方法 } } 2.何为方法的重写? 子类对父类继承过来的方法进行的覆盖、覆写的操作,就称为方法的重写, 3.方法重写应遵循的规则 [复习]方法声明的格式:权限修饰符 返值类型 方法名(形参列表) [throws 异常类型] {//方法体} 具体规则: ① 父类被重写的方法与子类重写的方法的方法名和形参列表必须相同。 ② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符 >子类不能重写父类中声明为private权限修饰的方法。 ③ 关于返回值类型: > 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型必须是void > 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须与被重写的方法的返回值类型相同 > 父类被重写的方法的返回值类型是引用数据类型(比如类),则子类重写的方法的返回值类型可以与被重写的方法的返回值 类型相同 或 是被重写的方法的返回值类型的子类 ④ (超纲)子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类 补充说明:方法体:没有要求。但是子类重写的方法的方法体必然与父类被重写的方法的不同 4.面试题:区分方法的重载(overload)与重写(override / overwrite) 重载:"两同一不同" 重写:继承以后,子类覆盖父类中同名同参数的方法 [类比]相同类型的面试题: throws /throw final /finally /finalize Collection /Collections String /stringBuffer /stringBuilder ArrayList /LinkedList HashMap /LinkedHashMap /Hashtable ... sleep()/ wait() ==/ equals() 同步 /异步Person public class Person { //属性 String name; private int age; //方法 public void eat(){ System.out.println("人吃饭"); } public void sleep(){ System.out.println("人睡觉"); } public void show(){ System.out.println("age = " + age); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //override public int info(){ return 1; } public Person info1() { return null; } } Student public class Student extends Person { //属性 String school; String Study; public void study(){ System.out.println("学生学习"); } public void eat(){ System.out.println("学生应该多吃有营养的食物"); } public void show1() { System.out.println("age : " + getAge()); } //重写针对于返回值的测试 public int info(){ return 1; } //错误 // public byte info(){ // return 1; // } // public long info(){ // return 1; // } public Student info1() { return null; } //错误 // public Object info1() { // return null; // } // public Person info1() { // return null; // } public void sleep(){ System.out.println("学生应该多睡,养大脑"); } } overrirdeTest public class overrirdeTest { public static void main(String[] args) { Student s1 = new Student(); s1.eat(); s1.sleep(); } }
关键字—super
当子类与父类有相同的属性时,执行编译时可以区分开
练习:
案例: 1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示 该类包括的属性:账号id,余额balance,年利率annualInterestRate; 包含的方法:访问器方法(getter和setter方法),返回月利率的方法getMonthlyInterest(),取款方法withdraw 写一个用户程序测试Account类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%的Account对象 使用withdraw方法提款30000元,并打印余额。 再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。 提示:在提款方法withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。 运行结果如图所示。 2、创建Account类的一个子类checkAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。 在CheckAccount类中重写withdraw方法,其算法如下: —————————————————————————————————————————— 如果(取款金额<账户余额) 可直接取款 如果(取款金额>账户余额) 计算需要透支的额度 提示用户超过可透支额的限额 ——————————————————————————————————————————— 要求:写一个用户程序测试CheckAccount类。 在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。 使用withdraw方法提款5000元,并打印账户余额和可透支额。 再使用withdraw方法提款18000元,并打印账户余额和可透支额 再使用withdraw方法提款3000元,并打印账户余额和可透支额。 提示: (1)子类CheckAccount的构造方法需要将从父类继承的3个属性和子类自己的属性全部初始化。 (2)父类Account的属性balance被设置为private,但在子类checkAccount的withdraw方法中需要修改它的值, 类图Account:运行结果如下图所示。
![]()
Account public class Account { private int id; private double balance; private double annualInterestRate; // 年利率 // public Account() {} public Account(int id, double balance, double annualInterestRate) { //super() this.id = id; this.balance = balance; this.annualInterestRate = annualInterestRate; } public int getId() { return id; } public void setId(int id) { this.id = id; } public double getBalance() { return balance; } // public void setBalance(double balance) { // this.balance = balance; // } public double getAnnualInterestRate() { return annualInterestRate; } public void setAnnualInterestRate(double annualInterestRate) { this.annualInterestRate = annualInterestRate; } public double getMonthlyInterest(){ return annualInterestRate / 12; } /** * 取钱操作 * @param amount 要取的钱数 */ public void withdraw(double amount){ if(balance >= amount && amount > 0) { balance -= amount; }else { System.out.println("余额不足!"); } } /** * 存钱的操作 * @param amount 要存的额度 */ public void deposit(double amount){ if(amount > 0){ balance += amount; } } } AccountTest public class AccountTest { public static void main(String[] args) { Account acct = new Account(1112,20000,0.045); acct.withdraw(30000); System.out.println("您的账户余额为:" + acct.getBalance()); acct.withdraw(2500); acct.deposit(3000); System.out.println("您的账户余额为:" + acct.getBalance()); System.out.println("月利率为" + acct.getMonthlyInterest()); } } CheckAccount public class CheckAccount extends Account{ private double overdraft; //可透支限额 public CheckAccount(int id, double balance, double annualInterestRate){ super(id, balance, annualInterestRate); //默认会调用父类空参的构造器,如果父类没有空参构造器,就可以调用无参的构造器 } public CheckAccount(int 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; } /** * 针对可透支的账户的取钱的操作 * @param amount 要取的钱数 */ public void withdraw(double amount){ if(getBalance() >= amount){ //错误的: getBalance() 是一个值 // getBalance() = getBalance() - amount; //正确的,但是直接重置账户余额可以容易误操作,直接调用取钱的方法会更加方便 // setBalance(getBalance() - amount); super.withdraw(amount); }else if(getBalance() + overdraft >= amount){ //注意一下先取那个钱的操作,先取透支额度当中的钱,再取余额当中的钱,因为运算中会涉及到余额,如果先取余额了,那么余额就为0了,这样运算,透支的就是要取的钱 overdraft -= amount - getBalance(); super.withdraw(getBalance()); }else{ System.out.println("超过可透支限额"); } } } CheckAccoutTest public class CheckAccoutTest { public static void main(String[] args) { CheckAccount checkAccount = new CheckAccount(1112,20000,0.045,5000); checkAccount.withdraw(5000); System.out.println("您的账户余额:" + checkAccount.getBalance()); // 15000.0 System.out.println("您的可透支余额:" + checkAccount.getOverdraft()); //5000.0 checkAccount.withdraw(18000); System.out.println("您的账户余额:" + checkAccount.getBalance()); //0.0 System.out.println("您的可透支余额:" + checkAccount.getOverdraft()); //2000.0 } }
面试题1(看执行结果):
public class interview01 {
public static void main(String[] args) {
new A(new B());
}
}
class A {
public A(){
System.out.println("A");
}
public A(B b){
this();
System.out.println("AB");
}
}
/**
* 打印顺序 new A(new B());
* B
* A
* AB
*/
//class B {
// public B(){
// System.out.println("B");
// }
//}
/**
* 执行顺序 new A(new B());
* A
* B
* A
* AB
*/
class B extends A {
public B(){
System.out.println("B");
}
}
面试题2(看执行结果):
public class interview02 {
public static void main(String[] args) {
Father f = new Father();
Son s = new Son();
System.out.println(f.getInfo()); //atguigu
System.out.println(s.getInfo()); //atguigu
s.test(); //atguigu atguigu
System.out.println("-------------------------");
s.setInfo("大硅谷");
System.out.println(f.getInfo()); //atguigu
System.out.println(s.getInfo()); //大硅谷
s.test(); //大硅谷 大硅谷
}
}
class Father {
private String info = "atguigu";
public String getInfo() {
// System.out.println( this.info + "9");
return info;
}
public void setInfo(String info) {
// System.out.println( this.info + "9");
this.info = info;
}
}
class Son extends Father {
private String info = "尚硅谷";
public void test(){
System.out.println(this.getInfo()); //this和super调用的都是父类的getInfo
System.out.println(super.getInfo()); //s这个对象把父类的info更改了
}
/**
* 加了getInfo之后的执行顺序,没重写之前改的是父类的info
* atguigu
* 尚硅谷
* 尚硅谷
* atguigu
* -------------------------
* atguigu
* 尚硅谷
* 尚硅谷
* 大硅谷 s.setInfo("大硅谷"); 只在父类当中有这个方法,此时更改的是父类的info
* @return
*/
public String getInfo(){
return info;
}
}
总结:
一、super关键字的使用 1.为什么需要super? 举例1:子类继承父类以后,对父类的方法进行了重写,那么在子类中,是否还可以对父类中被重写的方法进行调用? 可以! 举例2:子类继承父类以后,发现子类和父类中定义了同名的属性,是否可以在子类中区分两个同名的属性? 可以! 如何调用? 使用super关键字即可。 2.super的理解: 父类的 3.super可以调用的结构:属性、方法、构造器 具体的: 3.1 super调用属性、方法 子类继承父类以后,我们就可以在子类的方法或构造器中,调用父类中声明的属性或方法。(满足封装性的前提下) 如何调用呢?需要使用"super."的结构,表示调用父类的属性或方法。 一般情况下,我们可以考虑省略"super."的结构。但是,如果出现子类重写了父类的方法或子父类中出现了同名的属性时, 则必须使用"super."的声明,显式的调用父类被重写的方法或父类中声明的同名的属性。 3.2 super调用构造器 ① 子类继承父类时,不会继承父类的构造器。只能通过"super(形参列表)"的方式调用父类指定的构造器 ② 规定:"super(形参列表)",必须声明在构造器的首行。 ③ 我们前面讲过,在构造器的首行可以使用"this(形参列表),调用本类中重载的构造器, 结合②,结论:在构造器的首行,"this(形参列表)"和"super(形参列表)"只能二选一。 ④ 如果在子类构造器的首行既没有显示调用"this(形参列表)",也没有显式调用"super(形参列表)", 则子类此构造器默认调用"super()",即调用父类中空参的构造器。 ⑤ 由③和④得到结论:子类的任何一个构造器中,要么会调用本类中重载的构造器,要么会调用父类的构造器。 只能是这两种情况之一。 ⑥ 由⑤得到:一个类中声明有n个构造器,最多有n-1个构造器中使用了"this(形参列表)", 则剩下的那个一定使用"super(形参列表)"。 ---> 我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接的调用到父类的构造器。 也正因为调用过父类的构造器,我们才会将父类中声明的属性或方法加载到内存中,供子类对象使用。 二、子类对象实例化全过程 代码举例: class Creature{//生物类 //声明属性、方法、构造器 } class Animal extends Creature{ //动物类 } class Dog extends Animal{ //狗类 } class DogTest{ public static void main(string[] args){ Dog dog = new Dog(); dog.xxx(); dog.yyy = ....; } } 1.从结果的角度来看: 当我们创建子类对象后,子类对象就获取了其父类对象中所有的属性和方法,在权限允许的情况下,可以直接调用 2.从过程的角度来看: 当我们通过子类的构造器创建对象时,子类的构造器一定会直接或间接的调用到其父类的构造器,而其父类的构造器 同样会直接或间接的调用到其父类的父类的构造器,.....,直到调用了0bject类中的构造器为止。 正因为我们调用过子类所有的父类的构造器,所以我们就会将父类中声明的属性、方法加载到内存中,供子类的对象使用 问题:在创建子类对象的过程中,一定会调用父类中的构造器吗? yes! 3.问题:创建子类的对象时,内存中到底有几个对象? 就只有一个对象!即为当前new后面构造器对应的类的对象。
子类对象的实例化过程
面向对象特征之三:多态性
查看字节码文件:
编译的时候会认为我们还是调用的person里面的方法,注意前面的virtual是虚拟的意思,调用我们这个虚的方法,编译时方法,也即是父类当中的方法,运行时会动态绑定创建的对象的方法
虚拟方法调用
方法的重载与重写
instanceof 操作符
对象类型转换 (Casting )
这个地方的逻辑,传谁就造谁的对象,可以用于区分是登录还是注册的情况
总结:
1.如何理解多态性?
理解:理解为一个事物的多种形态。
生活举例:
> 女朋友:我想养一个宠物。
> 孩子:我想要一个玩具
> 老板:张秘书,安排一个技术科的同事,跟我一起下周出差。
2.Java中多态性的体现:
子类对象的多态性:父类的引用指向子类的对象。(或子类的对象赋给父类的引用)
比如:Person p2 = new Man();
3.多态性的应用:
多态性的应用:虚拟方法的调用
在多态的场景下,调用方法时,
编译时,认为方法是左边声明的父类的类型的方案(即被重写的方法)
执行时,实际执行的是子类重写父类的方法
简称为:编译看左边,运行看右边
4.多态性的使用前提:①要有类的继承关系 ②要有方法的重写
5.多态的适用性: 仅适用于方法,不适用于属性
6.多态的好处与弊端
6.1 弊端:
在多态的场景下,我们创建了子类的对象,也加载了子类特有的属性和方法。但是由于声明为父类的引用,
导致我们没有办法直接调用子类特有的属性和方法。
6.2 好处:
极大的减少了代码的冗余,不需要定义多个重载的方法。
举例:
class Account{
public void withdraw(){} //取钱
}
class CheckAccount extends Account{
//存在方法的重写
public void withdraw(){} //取钱
}
class SavingAccount extends Account{
//存在方法的重写
}
class Customer {
Account account;
public void setAccount(Account account) {// Account account = new CheckAccount();
this.account = account;
}
public Account getAccount(){
return accout;
}
}
class CustomerTest{
main(){
Customer cust = new Customer();
cust.setAccount(new CheckAccount());
cust.getAccount().witidraw();
}
}
7.instanceof的使用
/**
* 1.建议在向下转型之前使用instanceof进行判断,避免出现类型转换异常
* 2.格式: a instanceof A : 判断对象a是否是类A的实例
* 3.如果 a istanceof A 返回true,则:
* a instanceof superA 返回true,其中,A 是superA的子类
*/
面试题1:
FieldMethodTest
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count); //20
s.display();//20
//相当于Base b = new Sub();
Base b = s; //多态的特点,运行看右边,编译看左边,针对于方法来说的话,属性没有这样的特点
System.out.println(b == s); // true
System.out.println(b.count); //10
b.display();//20
Base b1 = new Sub();
System.out.println(b1.count); //10
b1.display(); //10
}
}
class Base{
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Base{
int count = 20;
public void display(){
System.out.println(this.count);
}
}
Object 类的使用
==操作符与equals方法
练习题:
public class UserTest {
public static void main(String[] args) {
User u1 = new User("Tom", 12);
User u2 = new User("Tom", 12);
// System.out.println(u1.equals(u2)); //false 重写equals前比较的是地址值
System.out.println(u1.equals(u2)); //true 重写equals后比较的是内容
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); //false
System.out.println(str1.equals(str2)); //true
File file1 = new File("d:\\abc.txt");
File file2 = new File("d:\\abc.txt");
System.out.println(file1.equals(file2)); //true
//数组上使用equals()
int[] arr = new int[10];
System.out.println(arr.equals(new int[20])); //false
System.out.println(arr.equals(new int[10])); //false
}
}
class User {
String name;
int age;
public User(){
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
//重写equals,手动实现
// @Override
// public boolean equals(Object obj) {
return super.equals(obj);
// if(this == obj){
// return true;
// }
// if(obj instanceof User){
// User user = (User)obj;
if(this.age == user.age && this.name.equals(user.name)){
return true;
}else {
return false;
}
// return this.age == user.age && this.name.equals(user.name);
// }
// return false;
// }
// IDEA自动实现
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
}
toString() 方法
ctrl+n调用出需要搜索的内容,从jdk8与jdk17里面
总结:
0bject类的概述 1.0bject类的说明 > 明确:java.lang.0bject > 任何一个Java类(除0bject类)都直接或间接的继承于0bject类 > 0bject类称为java类的根父类 > 0bject类中声明的结构(属性、方法等)就具有通用性 > 0bject类中没有声明属性 > 0bject类提供了一个空参的构造器 > 重点关注:0bject类中声明的方法 2.常用方法 重点方法:equals() \ toString() 了解方法:clone() \ finalize() 目前不需要关注:getClass()\hashCode()\ notify()\ notifyAll()\ wait()\ wait(xxx)\ wait(xxx,yyy) 面试题:final、finally、finalize 的区别 3.equals()的使用 3.1 适用性: 任何引用数据类型都可以使用。 3.2 java.lang.0bject类中equals()的定义 public boolean equals(object obj){ return (this == obj); } 3.3 子类使用说明: > 自定义的类在没有重写0biect中equals()方法的情况下,调用的就是0bject类中声明的equals(), 比较两个 对象的引用地址是否相同。(或比较两个对象是否指向了堆空间中的同一个对象实体) > 对于像String、File、Date和包装类等,它们都重写了0bject类中的equals()方法,用于比较两个对象的实体内容是否相等。 3.4 开发中使用说明: > 实际开发中,针对于自定义的类,常常会判断两个对象是否equals(),而此时主要是判断两个对象的属性值是否相等 所以:我们要重写0bject类的equals()方法。 > 如何重写: > 手动自己实现 >调用IDEA自动实现(推荐) 3.5 高频面试题:区分 == 和 equals() ==:运算符 ① 使用范围:基本数据类型、引用数据类型 ② 基本数据类型:判断数据值是否相等 int i1 = 10; int i2 = 10; sout(i1 == i2);//true char c1='A'; int i3 = 65; sout(c1 == i3);//true float f1 = 12.0F; int i4 = 12; sout(f1 == i4);//true 引用数据类型变量:比较两个引用变量的地址值是否相等。(或比较两个引用是否指向同一个对象实体) equals():方法 > 使用范围:只能使用在引用数据类型上。 > 具体使用:对于类来说,重写equals()和不重写equals()的区别。 4.toString()的使用 4.1 0bject类中toString()的定义: public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } 4.2 开发中的使用场景 > 平时我们在调用System.out.println()打印对象引用变量时,其实就调用了对象的toString() 4.3 子类使用说明: > 自定义的类,在没有重写0bject类的toString()的情况下,默认返回的是当前对象的地址值。 > 像String、File、Date或包装类等0bject的子类,它们都重写了0bject类的tostring(),在调用toString()时, 返回当前对象的实体内容。 4.4 开发中使用说明: > 习惯上,开发中对于自定义的类在调用toString()时,也希望显示其对象的实体内容,而非地址值。这时候,就需要重写0bject 类中的toString().public class ToString { public static void main(String[] args) { User u1 = new User("Tom", 23); System.out.println(u1.toString()); //com.atguigu07.object.toString.User@6504e3b2 ---> User{ name = Tom, age = 23 } System.out.println(u1); //com.atguigu07.object.toString.User@6504e3b2 String s1 = new String("hello"); System.out.println(s1.toString()); //hello 重写了的 File file = new File("d:\\abc.txt"); System.out.println(file); //d:\abc.txt Date date = new Date(); System.out.println(date); //Sun Mar 23 16:25:31 CST 2025 } } class User{ String name; int age; public User(){}; public User(String name, int age) { this.name = name; this.age = age; } //手动实现toString() // public String toString(){ // return "User{ name = " + name + ", age = " + age + " }"; // } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
练习题:
案例: 定义两个类,父类Geometric0bject代表几何形状,子类circle代表圆形。 写一个测试类,创建两个Circle对象,判断其颜色是否相等;利用equals方法判断其半径是否相等; 利用toString()方法输出其半径。
Geometric0bject
public class Geometric0bject {
protected String color;
protected double weight;
public Geometric0bject() {
color = "white";
weight = 1.0;
}
public Geometric0bject(String color, double weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
Circle
public class Circle extends Geometric0bject {
private double radius;
public Circle() {
// color = "white";
// weight = 1.0;
radius = 1.0;
};
public Circle(double radius) {
// color = "white";
// weight = 1.0;
this.radius = radius;
};
public Circle(String color, double weight, double radius) {
super(color, weight);
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
//求圆的面积
public double findArea(){
return 3.14 * radius * radius;
}
//重写equals
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj instanceof Circle){
Circle c = (Circle)obj;
return this.radius == c.radius;
}
return false;
}
//重写toString()
@Override
public String toString() {
return "Circle[radius = " + radius + "]";
}
}
CircleTest
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle(2.3);
Circle c2 = new Circle("red",2.0,3.4);
System.out.println("颜色是否相等?" + c1.getColor().equals(c2.getColor()));
System.out.println("半径是否相等?" + c1.equals(c2)); //equals当中比较的就是半径
System.out.println(c1); //Circle[radius = 2.3]
System.out.println(c1.toString()); //Circle[radius = 2.3]
}
}