封装
定义
Java中的封装(Encapsulation)是一种面向对象编程(Object-Oriented Programming,OOP)的概念,指的是将类的成员变量(属性)
和成员方法(方法)
隐藏起来,通过使用访问修饰符(如private、public、default、protected等)
来限制外部对类的直接访问,从而保护类的内部状态和行为。
封装是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,进对外公开接口来和对象进行交互
eg:电脑提供给用户的就只是一些很容易上手的功能:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,以此来完成日常事务。
但一个计算机,真正工作的是黑匣子里面的CPU、显卡、内存等一些硬件元件。
对于计算机使用者而言,不用去关心内部的核心不见,比如主板上的线路是如何布局的?CPU是如何设计的?
用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。
因此计算机厂商在出厂的时候,在外部套上壳子,将内部的实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可,这样能让用户操作更舒适,也更进一步得保护电脑主机。对于我们写的程序来说也是这样
访问修饰符
1. private ( 当前类访问权限 )
使用private修饰的成员只能在当前类的内部被访问。常用于修饰成员变量 ( 封装 )。
可以使用setXXX()和getXXX()来进行赋值和获取值,并可以在setXXX()中进行逻辑处理判断后在进行赋值
public class Demo{ public static void main(String[] args){ Person p = new Person(); // p.name = “tom”; // 报错,不可以直接使用私有属性 // p.age = 18; // 报错,不可以直接使用私有属性 } }class Person{ private String name; private int age; public setName(String name){ this.name = name; }public getName(){ return this.name; }public setAge(int age){ if(age < 0){ this.age = 0; }else{this.age = a; } }public int getAge(){ return this.age; } }
2. default ( 包访问权限 )
默认权限修饰符。使用 default 修饰的 成员或外部类 可以被 相同包下的其他类 访问。
3. protect ( 子类访问权限 )
使用 protect 修饰的 成员 既可以被 同一个包中的其他类 访问,也可以被 不同包中的子类 访问。常用于修饰 希望被子类重写的方法 ( override )。
4. public ( 公共访问权限 )
使用 public 修饰的 成员或者外部类 可以被 所有类 访问。
5. 访问控制级别表
private | default | protect | public | |
---|---|---|---|---|
同一个类中 | Y | Y | Y | Y |
同一个包中 | Y | Y | Y | |
子类中 | Y | Y | ||
全局范围内 | Y |
继承
定义
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
格式
class父类 { } class子类 extends父类 { }
分类
特点:
继承关系是传递的。若类C继承类B,类B继承类A(多层继承),则类C既有从类B那里继承下来的属性与方法,也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法。
继承来的属性和方法尽管是隐式的,但仍是类C的属性和方法。继承是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。
构造器 super()
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用super关键字调用父类构造器,系统会自动调用父类的无参构造器。
父类中的共有成员变量自变量可以使用,如果同名的变量子类没有,则可以直接使用父类的成员变量,否则需要使用super关键字
class Fu{ int i = 1; int c = 1; }class Zi extends Fu{ int j = 2; int c = 2; public void print(){ int a = 3; int c = 3; System.out.println(a);//3 System.out.println(j);//2 System.out.println(i);//1 System.out.println(c);//3 System.out.println(this.c);//2 System.out.println(super.c);//1} }class Demo{ public static void main(Sting[] args){ Zi zi = new Zi(); zi.print(); } }
子类中的所有的构造方法的第一行默认都有一个super()
class Fu{ String name; public Fu(){ name = “tom”; } }class Zi extends Fu{ public Zi(){ //super(); //调用父类的空参构造}public Zi(String name){ //super(); //调用父类的空参构造} }class Demo{ public static void main(String[] args){ Zi zi = new Zi(); System.out.println(zi.name);//tom // 创建子类对象的时候,会调用子类构造函数, //而子类构造函数方法中有隐藏的super()方法, //作用是调用父类的构造方法,目的是给父类的成员初始化 } }
多态
定义
在Java中,多态是面向对象编程中的一个重要概念,它允许不同类型的对象对同一方法进行不同的实现。具体来说,多态性指的是通过父类的引用变量来引用子类的对象,从而实现对不同对象的统一操作。
例如:狗和猫都是动物,动物共同的行为都有吃这个动作,而狗可以表现为啃骨头,猫则可以表现为吃老鼠。这就是多态的表现,即同一件事情,发生在不同对象的身上,就会产生不同的结果。
多态实现的条件
在Java中,要实现多态性,就必须满足以下条件:
继承关系
存在继承关系的类之间才能够使用多态性。多态性通常通过一个父类用变量引用子类对象来实现。
方法重写
子类必须重写(Override)父类的方法。通过在子类中重新定义和实现父类的方法,可以根据子类的特点行为改变这个方法的行为,如猫和狗吃东西的独特行为。
父类引用指向子类对象
使用父类的引用变量来引用子类对象。这样可以实现对不同类型的对象的统一操作,而具体调用哪个子类的方法会在运行时多态决定
例如,下面的案例是根据猫和狗叫的动作的不同,而实现的多态class Animal {public void sound() { System.out.println("动物发出声音"); } }class Dog extends Animal {@Overridepublic void sound() { System.out.println("狗发出汪汪声"); } }class Cat extends Animal {@Overridepublic void sound() { System.out.println("猫发出喵喵声"); } }public class Main {public static void main(String[] args) {Animal animal1 = new Dog(); // 父类引用指向子类对象Animal animal2 = new Cat(); // 父类引用指向子类对象animal1.sound(); // 输出:狗发出汪汪声animal2.sound(); // 输出:猫发出喵喵声} }//在这个示例中,Animal 类是父类,Dog 和 Cat 类是它的子类//通过将父类的引用变量分别指向子类对象,实现了多态性//在运行时,根据引用变量的实际类型来调用相应的子类方法,从而输出不同的声音
多态间子父类之间成员变量的关系
父类中有一个成员变量,子类中有一个成员变量,两个变量的名字不同 ——— 父类引用不能使用子类中特有的成员变量
父类中有一个成员变量,子类中有一个成员变量,两个变量的名字相同 ——— 父类引用只能使用自身带的成员变量,不能使用子类中成员变量
口诀:多态调用成员变量的时候,编译看左边,运行看左边 —— 意思是编译是否报错看等号左边的类对象里是否有该成员变量,运行是否能成功看左边的类对象里是否有该成员变量
class Fu{ int i = 1; int z = 1; }class Zi extends Fu{ int j = 2; int z = 2; }class Demo{ public static void main(String[] args){ Fu fu = new Zi(); System.out.println(fu.i);//1 //System.out.println(fu.j);//编译报错System.out.println(fu.z);//1} }
多态间子父类之间成员方法的关系
父类中有一个成员方法,子类中有一个成员方法,两个方法的名字不同 —— 父类引用不能使用子类中特有的成员方法
父类中有一个成员方法,子类中有一个成员方法,两个方法的名字相同 —— 父类引用可以使用子类中重写父类的方法
口诀:多态调用成员方法,编译看左边,运行看右边 —— 意思是编译是否报错看等号左边的类对象里是否有该成员变量,运行是否能成功看右边的类对象里是否有该成员变量
class Fu{ public void printFu{ System.out.println(“Fu”); }public void print{ System.out.println(“FU——”); } }class Zi extends Fu{ public void printZi{ System.out.println(“Zi”); }public void print{ System.out.println(“Zi——”); } }class Demo{ public static void main(String[] args){ Fu fu = new Zi(); fu.printFu();//Fu //fu.printZi();//编译错误 fu.print();//Zi} }
重写
什么是重写
在面向对象编程中,重写(Override)指的是子类重新定义和实现了从父类继承而来的方法,以改变方法的行为。子类通过重写方法可以提供自己特定的实现,使得父类方法的行为在子类对象的身上有不同的表现。
概念
继承关系
重写方法是基于父类和子类之间的继承关系。子类继承了父类的方法,包括方法的名称、参数列表和返回类型。
方法签名
重写的方法与父类的方法具有相同的方法签名,即方法的名称、参数列表和返回类型必须一致(当然,如果返回类型的对象本身的类型则可以不同,但是必须要有继承关系)。方法签名不包括方法体。
补充: 方法签名的组成:方法名和参数列表(形参类别、个数、顺序)
@Override注解
为了明确表明这是一个重写的方法,可以使用 @Override 注解来标记子类中的方法。该注解会在编译时检查是否满足重写条件,如果不满足会报错。
动态绑定
通过父类引用变量调用被子类重写的方法时,会根据实际引用的对象类型,在运行时动态绑定到相应的子类方法。
方法重写的规则:
方法名称、参数列表和返回类型必须与父类中被重写的方法相同
子类重写的方法的访问修饰符的权限不能低于父类中被重写方法的访问修饰符权限public > protected > 默认 > private
例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected。
重写的方法不能抛出比父类中被重写的方法更多或更宽泛的异常。子类中重写的方法可以抛出相同的异常或更具体的异常,或者不抛出异常
例如,如果父类的方法声明抛出 IOException,则子类中重写的方法可以抛出 IOException 或 FileNotFoundException,或者不抛出异常,但不能抛出比 IOException 更通用的异常,如 Exception。 重写的方法必须具有相同的方法体,或者可以进行方法体的扩展
子类中重写的方法可以调用父类中被重写的方法,使用 super 关键字
重写和重载的比较
重载的条件
方法名称相同:重载的方法必须具有相同的名称。
参数列表不同:重载的方法的参数列表必须不同。参数列表可以通过参数的类型、个数或顺序的不同来区分重载方法。
返回类型可以相同也可以不同:重载的方法可以具有相同的返回类型,也可以具有不同的返回类型。返回类型不是重载方法的区分标准。
重载方法必须定义在同一个类中。
方法的访问修饰符和异常:重载方法可以具有相同的访问修饰符(如 public、private、protected)和相同的抛出的异常。
重写和重载的区别:
概念上的不同
重载指的是在同一个类中,根据方法的参数列表的不同,定义多个具有
相同名称
但参数类型或个数不同
,并且相同或者不相同的返回类型
的方法。重写指的是子类重新定义和实现了从父类中继承的方法。重写的方法具有与父类方法
相同的名称
、相同的参数列表
和可以相同或具有继承关系的返回类型
。
使用上的不同
定义位置:重载方法定义在同一个类中,而重写方法定义在父类和子类之间。
方法签名:重载方法具有相同的名称,但方法签名(参数类型和个数)不同。重写方法具有相同的名称和方法签名。
继承关系:重载方法不涉及继承关系,可以在同一个类中定义。重写方法是在子类中对父类方法的重新定义和实现。
运行时调用:重载方法是根据方法的参数列表的不同进行静态绑定,在编译时确定。重写方法是根据对象的实际类型进行动态绑定,在运行时确定。
目的不同
重载方法用于在同一个类中实现相似功能但具有不同参数的方法。
重写方法用于子类重新定义父类方法的行为,以适应子类的特定需求。
总结来说,
重载是在同一个类中根据参数列表的不同定义多个具有相同名称但参数不同的方法,而重写是子类重新定义和实现了从父类继承的方法。
重载方法通过静态绑定在编译时确定调用,重写方法通过动态绑定在运行时确定调用。
重载用于实现相似功能但具有不同参数的方法,重写用于改变父类方法的行为以适应子类的需求。