equals方法与==、组合、多态、转型、简单工厂模式、final及abstract关键字
1. 继承中的父子类同名情况
1.1 父子类方法同名情况
如果父子类方法同名,那么子类便重写的其父类的同名方法,后期子类对象调用该方法执行的是子类重写的方法,子类可通过super关键字访问父类同名方法。
/**
* 功能:父子类方法同名情况
*/
//父类
class Father{
//默认无参构造器
public Father(){
super();//默认隐式调用其父类(Object类)构造器
}
//父类方法
public void test(){
System.out.println("执行了父类方法");
}
}
//子类
class Child extends Father{
//默认无参构造器
public Child(){
super();//默认隐式调用父类(Father类)构造器
}
//子类同名方法
public void test(){
super.test();//调用父类的test方法
System.out.println("执行了子类同名方法, 重写了父类test方法");
}
}
public class Test {
public static void main(String[] args) {
Child c=new Child();//创建子类对象
c.test();//调用的是子类重写的test方法
Father f1=new Father();//创建父类对象
f1.test();//调用的是父类test方法
Father f2=new Child();//创建子类对象将其赋值给父类的引用变量
f2.test();//调用的是子类重写的test方法,实现了多态
}
}
1.2 父子类变量同名情况
父子类同名变量会开辟各自的存储空间,互不干扰,子类可用过super关键字访问父类同名变量。
/**
* 功能:父子类变量同名情况
*/
//父类
class Father{
String test="父类的test变量";
//默认无参构造器
public Father(){
super();//默认隐式调用其父类(Object类)构造器
}
}
//子类
class Child extends Father{
String test="子类的同名test变量";
{
test=super.test;//子类访问父类的同名变量
}
//默认无参构造器
public Child(){
super();//默认隐式调用父类(Father类)构造器
}
}
public class Test {
public static void main(String[] args) {
Child c=new Child();//创建子类对象
System.out.println(c.test);;//访问的是子类同名的test变量
Father f1=new Father();//创建父类对象
System.out.println(f1.test);;//调用的是父类test变量
Father f2=new Child();//创建子类对象将其赋值给父类的引用变量
System.out.println(f2.test);;//调用的是父类的test变量,变量不能实现多态
}
}
2. 继承条件下构造方法的执行顺序
- 子类构造方法的第一条语句为默认的隐式super()语句,用来调用父类的无参构造方法
//父类
class Father{
//默认的隐式无参构造方法
public Father(){
super();//默认的隐式super语句,作用时调用父类的默认隐式无参构造方法(Father的父类是顶级类Object类)
}
}
//子类
class Child extends Father{
//默认的隐式无参构造方法
public Child(){
super();//默认的隐式super语句,作用是调用父类的默认隐式无参构造方法
}
}
- 可以改写为显式的super(arguments)语句来实现调用父类的有参构造方法
//父类
class Father{
//默认的隐式无参构造方法
public Father(){
super();//默认的隐式super语句,作用时调用父类的默认隐式无参构造方法(Father的父类是顶级类Object类)
}
//提供显式有参构造方法
public Father(String args){
}
}
//子类
class Child extends Father{
//默认的隐式无参构造方法
public Child(){
super("args");//显式的super语句,调用父类显式有参构造方法
}
}
- 也可以改写为显式的this(arguments)语句来实现调用本类的有参构造方法
//父类
class Father{
//默认的隐式无参构造方法
public Father(){
super();//默认的隐式super语句,作用时调用父类的默认隐式无参构造方法(Father的父类是顶级类Object类)
}
//提供显式有参构造方法
public Father(String args){
}
}
//子类
class Child extends Father{
//默认的隐式无参构造方法
public Child(){
super();//隐式的super语句,调用父类隐式无参构造方法
}
//显式有参构造方法
public Child(String args){
super();//隐式的super语句,调用父类隐式无参构造方法
}
//显式有参构造方法
public Child(String args, String argss){
this(args);//调用本类显式有参构造方法
}
}
- 构造方法中不能同时出现this与super语句来调用构造方法,但可以同时出现this和super语句来调用属性或非构造方法,但调用构造方法的语句必须是第一条语句
//父类
class Father{
//默认的隐式无参构造方法
public Father(){
super();//默认的隐式super语句,作用时调用父类的默认隐式无参构造方法(Father的父类是顶级类Object类)
}
//提供显式有参构造方法
public Father(String args){
}
}
//子类
class Child extends Father{
//默认的隐式无参构造方法
public Child(){
super();//隐式的super语句,调用父类隐式无参构造方法
}
//显式有参构造方法
public Child(String args){
super();//隐式的super语句,调用父类隐式无参构造方法
}
//显式有参构造方法
public Child(String args, String argss){
super();//报错
this(args);//调用本类显式有参构造方法
}
}
//父类
class Father{
//父类成员变量
String test;
//默认的隐式无参构造方法
public Father(){
super();//默认的隐式super语句,作用时调用父类的默认隐式无参构造方法(Father的父类是顶级类Object类)
}
//提供显式有参构造方法
public Father(String args){
}
}
//子类
class Child extends Father{
//子类成员变量
String test;
//默认的隐式无参构造方法
public Child(){
super();//隐式的super语句,调用父类隐式无参构造方法
}
//显式有参构造方法
public Child(String args){
super();//隐式的super语句,调用父类隐式无参构造方法
}
//显式有参构造方法
public Child(String args, String argss){
this(args);//调用本类显式有参构造方法
String test=this.test;//通过this调用本类属性
test=super.test;//通过super调用父类属性
}
}
- 建议显式添加默认的隐式无参构造方法
3. ==与equals()
==与equals()都可以对两个变量进行内容判断,如果相同返回true,如果不同返回false
3.1 ==
因为==操作的是栈数据,所以如果变量是基本数据类型,那么比较的是该变量的数值,如果是引用数据类型,比较的则是两个变量的地址值
//测试类
class Test2{
//成员变量
int b;
//默认的无参构造方法
public Test2(){
}
//构造方法重载
public Test2(int b){
this.b=b;
}
}
public class Test {
public static void main(String[] args) {
//基本数据类型
int a1=10;
int a2=20;
int a3=10;
System.out.println(a1==a2);//输出结果:false
System.out.println(a1==a3);//输出结果:true
System.out.println(a1);//输出结果:10
System.out.println(a2);//输出结果:20
System.out.println(a3);//输出结果:10
//引用数据类型
Test2 b1=new Test2(10);
Test2 b2=new Test2(20);
Test2 b3=new Test2(10);
System.out.println(b1==b2);//输出结果:false
System.out.println(b1==b3);//输出结果:false
System.out.println(b1);//输出结果:cn.khue.csdn.Test2@3f3afe78
System.out.println(b2);//输出结果:cn.khue.csdn.Test2@7f63425a
System.out.println(b3);//输出结果:cn.khue.csdn.Test2@36d64342
}
}
3.2 equals()
equals()操作的是堆数据,但是Object类的equals()方法比较的两个变量的地址值,所以在调用该方法时需要重写
//测试类
class Test2{
//成员变量
int b;
//默认的无参构造方法
public Test2(){
}
//构造方法重载
public Test2(int b){
this.b=b;
}
}
public class Test {
public static void main(String[] args) {
//引用数据类型
Test2 b1=new Test2(10);
Test2 b2=new Test2(20);
Test2 b3=new Test2(10);
System.out.println(b1.equals(b2));//输出结果:false
System.out.println(b1.equals(b3));//输出结果:false
}
}
重写equals方法后:
//测试类
class Test2{
//成员变量
int b;
//默认的无参构造方法
public Test2(){
}
//构造方法重载
public Test2(int b){
this.b=b;
}
//equals方法重写
@Override
public boolean equals(Object obj) {
Test2 t2=(Test2)obj;//因为传进的对象为Object类型,而我们要比较的时Test2类,所以需要向下转型
//判断传入的对象是否为空,如果为空,直接返回false
if(t2==null){
return false;
}
//判断传入的对象是否是同一个对象,如果是,直接返回true
if(this==t2){
return true;
}
//判断两个对象的内容是否相同,相同返回true,不同返回false
if(t2.b==this.b){
return true;
}else{
return false;
}
}
}
public class Test {
public static void main(String[] args) {
//引用数据类型
Test2 b1=new Test2(10);
Test2 b2=new Test2(20);
Test2 b3=new Test2(10);
System.out.println(b1.equals(b2));//输出结果:false
System.out.println(b1.equals(b3));//输出结果:true
}
}
4. 面试题_谈谈重载与重写
对象 | 英文名 | 实现位置 | 作用 | 修饰符 | 返回值类型 | 方法名 | 参数列表 | 抛出异常类型 | 方法体 |
---|---|---|---|---|---|---|---|---|---|
重载 | overload | 同类中 | 功能相同,提高效率 | 无关 | 无关 | 相同 | 不同 | 无关 | 不同 |
重写 | override | 子类中 | 父类的方法无法满足子类的需求时 | 权限大于等于 | 等级小于等于 | 相同 | 相同 | 等级小于等于 | 不同 |
5. 组合
5.1 作用
组合顾名思义就是将多个对象组合到一个新对象,以实现更复杂强大的功能,提高代码复用性。
5.2 与继承的区别
当类之间的关系为“is”时,一般使用继承,比如:dog is animal,那么dog类可以继承animal类
当类之间的关系为“has”时,一般使用组合,比如:computer has cpu、memory、mainboard、screen… 那么computer类可以由cpu类、memory类、mainboard类等组合而成
5.3 实现方式
//CPU类
class Cpu{
String cBrand;//cpu品牌
public Cpu(){}//无参构造方法
//有参构造方法
public Cpu(String cBrand){
this.cBrand=cBrand;
}
}
//memory类
class Memory{
String mBrand;//内存品牌
public Memory(){}//无参构造方法
//有参构造方法
public Memory(String mBrand){
this.mBrand=mBrand;
}
}
//mainboard类
class Mainboard{
String mbBrand;//主板品牌
public Mainboard(){}//无参构造方法
//有参构造方法
public Mainboard(String mbBrand){
this.mbBrand=mbBrand;
}
}
//computer类
class Computer{
Cpu cpu=new Cpu();//电脑的cpu
Memory memory=new Memory();//电脑的内存
Mainboard mainboard=new Mainboard();//电脑的主板
public Computer(){}//无参构造方法
//有参构造方法
public Computer(String cBrand, String mBrand, String mbBrand){
cpu.cBrand=cBrand;
memory.mBrand=mBrand;
mainboard.mbBrand=mbBrand;
}
//展示电脑的配置
public void show(){
System.out.println("My computer configuration:\nCPU:"+cpu.cBrand+"\nMemory: "+memory.mBrand+"\nMainboard: "+mainboard.mbBrand);
}
}
public class Test {
public static void main(String[] args) {
Computer computer=new Computer("Inter","Samsung","MSI");
computer.show();
}
}
/*------------------------
输出结果:
My computer configuration:
CPU:Inter
Memory: Samsung
Mainboard: MSI
--------------------------*/
6. 多态
多态是同一个行为具有多个不同表现形式或形态的能力。
6.1 作用
减少重载方法数量,增强可扩充性及可替换性,降低耦合度
6.2 实现方式
- 存在继承关系
- 子类重写父类方法
- 将子类的对象赋给父类的引用变量
//父类
class Father{
public void test(){
System.out.println("父类");
}
public void show(Father f){
f.test();
}
}
//子类1
class Child1 extends Father{
//重写父类方法
public void test(){
System.out.println("子类1");
}
}
//子类2
class Child2 extends Father{
//重写父类方法
public void test(){
System.out.println("子类2");
}
}
//子类3
class Child3 extends Father{
//重写父类方法
public void test(){
System.out.println("子类3");
}
}
public class Test {
public static void main(String[] args) {
Father f1=new Father();
f1.show(new Child1());//实现多态 输出结果:子类1
}
}
6.3 使用场景
- 使用父类作为方法的形参,实参可以是任意子类。示例代码如6.2示例代码
- 使用父类作为方法的返回值类型,实际返回的可以使任意子类的对象。示例代码如下:
//父类
class Father{
public void test(){
System.out.println("父类");
}
public Father show(){
test();
return new Father();
}
}
//子类1
class Child1 extends Father{
//重写父类方法
public void test(){
System.out.println("子类1");
}
}
//子类2
class Child2 extends Father{
//重写父类方法
public void test(){
System.out.println("子类2");
}
}
//子类3
class Child3 extends Father{
//重写父类方法
public void test(){
System.out.println("子类3");
}
}
public class Test {
public static void main(String[] args) {
Child1 c1=new Child1();
c1.show();//实现多态 输出结果:子类1
}
}
7. 向上转型
- 基本数据类型自动转换,优先级:byte、char、short—>int—>long—>float—>double
- 引用数据类型自动转换,但转换后的父类引用对象不能访问子类特有的属性及方法
//父类
class Father{
//父类的属性
String fname="我是父类的属性";
//父类的方法
public void ftest(){
System.out.println("我是父类的方法");
}
}
//子类
class Child extends Father{
//子类的属性
String cname="我是子类自己的属性";
//子类继承的方法(重写了)
public void ftest(){
System.out.println("我是子类重写父类的方法");
}
//子类自己的方法
public void ctest(){
System.out.println("我是子类自己的方法");
}
}
public class Test {
public static void main(String[] args) {
//基本数据类型向上转型
int a1=10;
double a2=a1;//int型数据自动转换成double型数据
System.out.println(a2);//输出结果:10.0
//引用数据类型向上转型
Father f=new Child();//子类自动转换成父类
System.out.println(f.fname);//输出结果:我是父类的属性
//System.out.println(f.cname);//报错,无法访问
f.ftest();//输出结果:我是子类重写父类的方法
//f.ctest();//报错,无法访问
}
}
8. 向下转型
- 基本数据类型需要使用(type)进行强制转换
- 引用数据类型向下转型之前必须先向上转型,而且向上转型的子类是必须是之后向下转型的子类
//父类
class Father{
}
//子类1
class Child1 extends Father{
}
//子类2
class Child2 extends Father{
}
public class Test {
public static void main(String[] args) {
//基本数据类型向下转型
double a1=10.52;
int a2=(int)a1;//int型数据自动转换成double型数据
System.out.println(a2);//输出结果:10
//引用数据类型向下转型
//Child c=(Child) new Father();//报错,java.lang.ClassCastException: class cn.khue.csdn.Father cannot be cast to class cn.khue.csdn.Child (cn.khue.csdn.Father and cn.khue.csdn.Child are in unnamed module of loader 'app')at cn.khue.csdn.Test.main(Test.java:42)
//因为子类的向下转型必须先要向上转型
Father f1=new Child1();//先向上转型
Child1 c1=(Child1)f1;//再向下转型
Father f2=new Child2();//先向上转型
Child1 c2=(Child1)f2;//再向下转型,报错:java.lang.ClassCastException: class cn.khue.csdn.Child2 cannot be cast to class cn.khue.csdn.Child1 (cn.khue.csdn.Child2 and cn.khue.csdn.Child1 are in unnamed module of loader 'app')at cn.khue.csdn.Test.main(Test.java:38)
//因为之前的向上转型的子类必须与之后向下转型的子类一样才行
}
}
当然,为了提高效率,我们可以在向下转型之前使用instanceof进行判断
//父类
class Father{
}
//子类1
class Child1 extends Father{
}
//子类2
class Child2 extends Father{
}
public class Test {
public static void main(String[] args) {
//基本数据类型向下转型
double a1=10.52;
int a2=(int)a1;//int型数据自动转换成double型数据
System.out.println(a2);//输出结果:10
//引用数据类型向下转型
Father f1=new Child1();
Father f2=new Child2();
//父类引用变量类型判断
System.out.println(f1 instanceof Child1);//输出结果:true
System.out.println(f1 instanceof Child2);//输出结果:false
System.out.println(f1 instanceof Father);//输出结果:true
System.out.println(f2 instanceof Father);//输出结果:true
System.out.println(f1 instanceof Object);//输出结果:true
System.out.println(f2 instanceof Object);//输出结果:true
System.out.println(f1 instanceof Scanner);//报错,因为instanceof右侧的类只能是与左侧对象有继承关系才行
}
}
9. 简单工厂模式
简单工厂模式也叫静态工厂方法
9.1 作用
不同new关键字,而通过一个静态方法来对外提供一个自身实例(对象)
9.2 实现方式
//父类
class Father{
//简单工厂模式
public static Father sFactoryM(String type){
switch (type){
case "c1":
return new Child1();
case "c2":
return new Child2();
default:
return new Father();
}
}
}
//子类1
class Child1 extends Father{
}
//子类2
class Child2 extends Father{
}
public class Test {
public static void main(String[] args) {
//通过new关键字创建对象
Father f1=new Father();
//通过静态工厂方法创建对象
Father f2=Father.sFactoryM("");
}
}
10. final关键字
final是finally的意思,即最后的、最终的、不再变化的意思
10.1 修饰对象
对象 | final | 效果 |
---|---|---|
类 | 可以修饰 | 该类不能创建子类 |
方法 | 可以修饰 | 该方法不能被子类重写 |
变量 | 可以修饰 | 该变量成为常量,初始化后其值不能被修改 |
构造方法 | 无法修饰 | 无 |
代码块 | 无法修饰 | 无 |
注:
- final如果修饰的引用数据类型的变量,那么该变量所指向的对象的变量的值仍可以被改变,而该变量所指定的对象的地址值无法被修改。
- 虽然构造方法不能用final修饰,但我们可以使用关键字private修饰构造方法,来实现外部只能通过类名来访问该类的方法或变量(但内部类还是可以访问该类的构造方法的)。
10.2 常用的final实例
10.2.1 常用的final类
10.2.2 常用的final方法
10.2.3 常用的final变量
11. abstract关键字
abstract是抽象的意思,在Java中,抽象在某种意义上可以理解为不能被实例化
11.1 修饰对象
对象 | final | 效果 |
---|---|---|
类 | 可以修饰(抽象类) | 该类无法创建对象(实例) |
方法 | 可以修饰(抽象方法) | 强制子类implements(实现)该方法或强制修改子类为抽象类 |
变量 | 无法修饰 | 无 |
构造方法 | 无法修饰 | 无 |
代码块 | 无法修饰 | 无 |
注:
- 虽然抽象类自身无法创建对象,但可以通过子类的向上转型创建其对象,且抽象类存在构造方法供其子类访问。
- 抽象类最少一个抽象方法也没有,最多其内部方法都为抽象方法(除构造方法外)。
- 父类的方法是抽象的,子类的方法需要implements(实现)该方法,父类的方法不是抽象的,子类的方法可以override(重写)该方法。
- 包含抽象方法的类必须是抽象类,因为如果是非抽象类,那么该类便可以实例化,而实例化之后便可以访问该类的抽象方法,但抽象方法在本类中并没有方法体,必须让子类实现,所以,该类必须是抽象类。