目录
(1)什么封装:
(2)封装的好处:
(3)private、public、protected的意义:
(4)什么是继承:
(5)什么时候用继承:
(6)继承的特点:
1、单继承:
2、非私有成员变量和成员方法
3、访问私有变量
(7)构造方法和成员方法的区别:
(8)继承中成员变量和成员方法以及构造方法的访问特点:
(9)方法的重写:
封装是面向对象编程中的一种重要概念,它是指将数据(属性)和操作数据的方法(行为)封装在一个单独的类中,同时对外部隐藏类的内部细节,只暴露出有限的接口供其他类进行交互。封装使得对象的使用者无需了解类的内部实现细节,只需要通过暴露的接口来使用对象,这样可以降低代码的复杂性,增加代码的可维护性和可复用性。
在封装中,类将数据成员声明为私有(private),这意味着只有类的内部成员可以直接访问这些数据,而其他类无法直接访问。对外部类而言,私有数据成员是不可见的,只能通过公有的方法(公有接口)来访问和修改数据。
隐藏实现细节:通过封装,类可以隐藏其内部实现细节,只暴露有限的接口给外部,这样可以防止外部类直接访问和修改类的私有数据,从而保护类的数据安全。
简化接口:封装可以将复杂的实现细节隐藏起来,对外部类提供简单的接口,使得类的使用更加简单和直观。
提高可维护性:封装可以将类的内部实现和外部接口分离,使得对类的修改不会影响外部类的使用,从而提高代码的可维护性。
增强代码的复用性:封装可以将类的实现细节封装起来,使得类可以在不同的应用场景中被复用,而不需要重新编写代码。
private
:表示私有成员,只能在类的内部访问,其他类无法直接访问。在Java中,可以使用 private
关键字来将类的成员声明为私有的。public class MyClass {
private int privateVar; // 私有成员变量
private void privateMethod() { // 私有方法
// 仅在类的内部可访问
}
}
public
:表示公共成员,可以在类的内部和外部直接访问。在Java中,类的成员默认是公共的,如果不想将成员设为公共的,就需要使用其他访问控制修饰符。public class MyClass {
public int publicVar; // 公共成员变量
public void publicMethod() { // 公共方法
// 在类的内部和外部都可以访问
}
}
protected
:表示受保护成员,可以在类的内部访问,以及派生类(子类)中访问。在Java中,使用 protected
关键字来将类的成员声明为受保护的。public class MyClass {
protected int protectedVar; // 受保护成员变量
protected void protectedMethod() { // 受保护方法
// 在类的内部和派生类中都可以访问
}
}
总结:
private
:私有成员,只能在类的内部访问。public
:公共成员,可以在类的内部和外部直接访问。protected
:受保护成员,可以在类的内部访问,以及派生类中访问。举个例子,其实之前的文章也有!
假设我们要创建一个简单的汽车类来表示汽车的基本信息,包括品牌、颜色和价格。我们可以使用封装来隐藏汽车类的内部细节,并通过公共接口来访问和修改汽车的属性。
public class Car {
private String brand; // 品牌(私有属性)
private String color; // 颜色(私有属性)
private double price; // 价格(私有属性)
// 构造方法
public Car(String brand, String color, double price) {
this.brand = brand;
this.color = color;
this.price = price;
}
// 获取品牌(公共方法)
public String getBrand() {
return brand;
}
// 获取颜色(公共方法)
public String getColor() {
return color;
}
// 获取价格(公共方法)
public double getPrice() {
return price;
}
// 设置价格(公共方法)
public void setPrice(double price) {
this.price = price;
}
}
(这些方法idea可以生成快捷方式alt+insert)。使用 private
关键字将汽车类的品牌、颜色和价格属性声明为私有的,这样外部的代码无法直接访问这些属性。然后,提供了公共的方法(getter 和 setter 方法)来访问和修改这些属性。
//创建对象这个直接调用构造方法进行赋值
Car car = new Car("Toyota", "Red", 25000.0);
//获取对象的信息调用方法
String brand = car.getBrand();
System.out.println("品牌:" + brand); // 输出:品牌:Toyota
继承是面向对象编程中的一种重要概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法,从而可以在子类中重用父类的功能并添加自己的特定功能。继承是面向对象编程的三大特性之一,另外两个是封装和多态。
在继承中,子类可以继承父类的所有非私有成员(属性和方法),包括数据成员和成员函数。子类可以直接使用继承自父类的属性和方法,也可以在子类中重写(覆盖)父类的方法以实现特定的行为。
继承的主要目的是代码重用和层次化组织。通过继承,我们可以将共性的代码放在父类中,减少重复编写相似代码的工作量。同时,继承也使得代码的结构更加清晰,将不同的类组织成一个类的层次结构,提高了代码的可读性和可维护性。
格式:
class 子类 extends 父类{
}
举个例子打个比方!
假设有一个动物园的模拟程序,我们需要在程序中创建各种动物,其中有不同种类的动物,例如狗、猫和鸟等。每种动物都有一些共同的属性和行为,比如它们都有名字和吃食物的行为,但同时也有一些独特的行为,比如狗会汪汪叫,猫会喵喵叫,鸟会飞。
在这个例子中,我们可以创建一个基类 (也就是父类)Animal 表示所有动物,其中包含共同的属性和方法,然后再创建各个子类继承自 Animal 类,每个子类可以添加自己的特有属性和方法。
// 基类 Animal 表示所有动物
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类 Dog 继承自 Animal
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void bark() {
System.out.println("汪汪!汪汪!");
}
}
// 子类 Cat 继承自 Animal
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
public void meow() {
System.out.println("喵喵!喵喵!");
}
}
// 子类 Bird 继承自 Animal
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(name + " is flying.");
}
}
然后我们创建小动物叭!
public class ZooSimulation {
public static void main(String[] args) {
Dog dog = new Dog("小黑");
Cat cat = new Cat("小花");
Bird bird = new Bird("小鸟");
dog.eat(); // 小黑 is eating.
dog.bark(); // 汪汪!汪汪!
cat.eat(); // 小花 is eating.
cat.meow(); // 喵喵!喵喵!
bird.eat(); // 小鸟 is eating.
bird.fly(); // 小鸟 is flying.
}
}
通过继承,我们可以将共同的属性和方法放在基类 Animal 中,减少了重复编写代码的工作量,同时每个子类也可以添加自己特有的行为。这样,我们可以更加方便地创建不同种类的动物,并且让程序更加简洁和易于扩展。
存在类之间的"是一种"关系:当一个类(子类)是另一个类(父类)的特殊化或具体化时,可以考虑使用继承。例如,狗类是动物类的一种,猫类是动物类的一种,这时候可以将动物类作为父类,而狗类和猫类作为子类,从而实现代码的重用和层次化组织。
存在共同的属性和方法:当多个类拥有相同的属性和方法时,可以将这些共同的部分抽象出来放在一个父类中,然后让子类继承这些共同的属性和方法。这样可以减少代码的重复,提高代码的可维护性和可读性。(加上一个限制,满足子类是父类的一种,最起码要有关系吧,就比方说一个小狗类,有名字name,有年龄age,一个老师类,也有名字name,有年龄age,要说你定义一个类里面属性有name和age两个属性,其实也没错但是就是觉得有一点怪怪的是叭)
java只能单继承,不支持多继承,但是可以多层继承(什么是多层继承,就是子类A继承父类B,而父类B又继承与父类(有点迷哈哈哈,那把它当成爷爷叭)C),画个图叭!
这样蒋其实A也可继承到C的属性和方法(是这样滴应该)但是举个反例!假设有以下三个类:
好的,让我用一个简单的例子来解释为什么Java只能单继承,如果允许多继承会出现什么问题。
假设有以下三个类:
1、Animal类:代表动物,具有一个eat()方法。
public class Animal {
public void eat() {
System.out.println("Animal is eating");
}
}
2、Bird类:代表鸟类,继承自Animal类,并且具有一个fly()方法
public class Bird extends Animal {
public void fly() {
System.out.println("Bird is flying");
}
}
3、Fish类:代表鱼类,继承自Animal类,并且具有一个swim()方法。
public class Fish extends Animal {
public void swim() {
System.out.println("Fish is swimming");
}
}
这些没有毛病吧,现在,假设我们希望创建一个新的类,既能够飞行又能够游泳(飞鱼!!!),我们可能会想让这个类同时继承自Bird类和Fish类,从而同时具有fly()方法和swim()方法。
public class FlyingFish extends Bird, Fish {
// ...
}
然而,这样的多重继承会带来一些问题:
1、方法冲突:由于FlyingFish类同时继承自Bird类和Fish类,它可能会继承到两个父类中具有相同方法名的方法(例如fly()和swim()),这样就会产生冲突,不知道该调用哪个父类的方法。
2、命名冲突:如果多个父类中存在相同名称的成员变量或常量,也会导致命名冲突。
为了避免上述问题,Java采用了单继承的机制,一个类只能直接继承自一个父类。如果需要多重继承的功能,可以使用接口来实现,通过接口可以使一个类实现多个接口,从而在一定程度上解决了多重继承的问题。但是接口只能定义抽象方法和常量,不能包含成员变量和方法的具体实现,从而遵循了单继承的限制。
继承体系:
就单单看应该D来讲,他的父类是B,它可以直接使用B里面非私有成员变量和成员方法,当然D也可以间接的使用A的非私有成员变量和成员方法,这里讲一下虽然B和C的父亲一样,但是D不能用C里面的变量和方法(叔叔不是爸爸,怎么会疼你,是不是乖乖!)
此外讲一下!如果一个类没有写他继承于谁,只是简简单单的一个独立的类,那么问题来了他有没有父类呐,就比方蒋这个Student类没有说他继承谁
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
答案是有的!在Java里面,所以的类直接或者间接的继承于object类
当一个子类继承自一个父类时,它继承了父类的所有非私有成员方法和成员变量。子类可以通过继承来直接使用这些成员方法和成员变量,而无需重新定义它们。如下面的代码
//父类
class Parent {
// 成员变量
int num = 10;
// 成员方法
void display() {
System.out.println("Parent class method");
}
}
//子类
class Child extends Parent {
// 子类可以直接调用父类的成员变量和成员方法
void show() {
System.out.println("Child class method");
System.out.println("Parent class variable num: " + num);
display();
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.show();
}
}
Child类继承自Parent类。在Child类的show()方法中,直接调用了父类的成员变量num和成员方法display(),并且可以正常地输出父类的数据和调用父类的方法。那么一个问题来了,怎么可以访问私有变量呐
如果父类B中的成员变量是public或protected的,子类A可以直接继承并访问这些成员变量。而且如果父类B中的成员变量是默认访问修饰符(包级私有,即没有明确修饰符)的,子类A可以继承并访问这些成员变量,前提是子类A和父类B在同一个包下。
但是子类A无法直接继承父类B中的私有成员变量。私有成员变量只能在父类B中被访问和修改,子类A无法直接访问它们。但是子类A可以通过父类B提供的公有方法来间接访问和修改私有成员变量。
假设有两个类,一个是动物
类(Animal),另一个是狗
类(Dog)。狗
类是动物
类的子类,也就是说,狗
类继承了动物
类的属性和方法。在动物
类中,有一个私有成员变量age
表示动物的年龄,以及一个公有方法getAge()
用于获取动物的年龄。
class Animal {
private int age;
public Animal(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
在狗
类中,我们可以直接使用继承自父类动物
的公有方法getAge()
来获取狗的年龄。
class Dog extends Animal {
private String breed;
public Dog(int age, String breed) {
super(age);
this.breed = breed;
}
public String getBreed() {
return breed;
}
public static void main(String[] args) {
Dog dog = new Dog(3, "金毛");
System.out.println("狗的年龄:" + dog.getAge());
System.out.println("狗的品种:" + dog.getBreed());
}
}
在上面的例子中,Dog
类继承了Animal
类,因此它可以直接使用父类Animal
中的公有方法getAge()
来获取动物的年龄。同时,Dog
类也可以定义自己的私有成员变量和方法,比如在上面的例子中,我们定义了一个私有成员变量breed
表示狗的品种,以及一个公有方法getBreed()
用于获取狗的品种。
总结:
首先我们要明白一点父类里面有什么,构造方法和成员变量以及成员方法,然后子类到底可以继承父类的什么呐,是非私有的全部的内容嘛?是私有的就都不可以继承嘛?
1、构造方法:私有和非私有,不管是什么修饰符,子类都不能继承。(因为构造方法要和类名一致,子类的名字总不能和父类的名字一样叭!)
2、成员变量:私有和非私有,子类都可以继承下来,但是不可以直接调用(要明白继承和直接调用是不同的意思。上面有例子,是可以通过非私有的方法去进项调用)
3、成员方法:私有和非私有,如果是私有的子类就不能继承下来,只能继承非私有的。
1、构造方法 (Constructor): 构造方法是一种特殊类型的方法,用于创建和初始化对象时自动调用的。它的主要目的是初始化对象的成员变量,为对象提供合理的初始状态。在Java中,构造方法的名称与类名相同,没有返回类型(包括void),且不能被直接调用,而是在创建对象时由new关键字隐式调用。每当创建一个新对象时,都会调用该对象的构造方法。如果在类中没有定义构造方法,编译器会默认提供一个无参构造方法。但是,如果在类中定义了自定义构造方法,那么默认的无参构造方法将不再自动生成,需要显式定义。
2、成员方法 (Instance Method): 成员方法是定义在类中的普通方法,用于执行特定的操作和功能。它们必须通过对象来调用,因为它们操作对象的状态或行为。成员方法可以访问对象的成员变量和其他成员方法,并且可以返回一个值(包括void)。成员方法是类的行为,用于操作类的数据和实现类的功能。在Java中,成员方法必须在对象上调用,通过使用点号语法来实现,例如:objectName.methodName()
。
总结:
1、成员变量:就近原则:谁离我近我用谁
父类
public class Fu {
String name="fu";
}
子类
public class Zi extends Fu {
String name = "Zi";
public void show() {
String name = "zishow";
System.out.println(name);
}
}
测试类
public class test {
public static void main(String[] args) {
Zi zi=new Zi();
zi.show();
}
}
调用show方法后会输出哪个name,就近原则:谁离我近我用谁,肯定是zishow,注释后是子类里面的zi,在注释是父类里面的Fu。
我们修改一下子类:
public class Zi extends Fu {
String name = "Zi";
public void show() {
String name = "zishow";
//输出局部变量
System.out.println(name);
//输出成员变量----->this(方法的调用者)
System.out.println(this.name);
//输出父类的成员变量------>super
System.out.println(super.name);
}
}
然后运行test类,下面是输出结果:
2、成员方法:就近原则:谁离我近我用谁
person类(父类)
public class person {
public void eat(){
System.out.println("吃东西!");
}
public void drank(){
System.out.println("喝水!");
}
}
teacher类(子类)
public class teacher extends person {
public void show(){
//1
eat();
drank();
//2
this.eat();
this.drank();
//3
super.eat();
super.drank();
/*首先讲一下1和2:其实1也有this,但是可以省略,他们两个是一样的首先在方法的调用者的类里面去找
eat和drink这两个方法,但是结果里面没有,然后看到他有一个父类就在去他的父类里面找
然后3呐,就是直接去方法调用者的父类里面找*/
}
}
测试类
public class test {
public static void main(String[] args) {
teacher t=new teacher();
t.show();
}
}
输出:
在teacher类里面解释了它如何运行的,这里没有什么区别是因为子类里面没有和父类重名的方法,我们修改一下子类从新测试就可以直观的感觉到。
子类:
public class teacher extends person {
public void show(){
//1
eat();
drank();
//2
this.eat();
this.drank();
//3
super.eat();
super.drank();
/*首先讲一下1和2:其实1也有this,但是可以省略,他们两个是一样的首先在方法的调用者的类里面去找
eat和drink这两个方法,但是结果里面没有,然后看到他有一个父类就在去他的父类里面找
然后3呐,就是直接去方法调用者的父类里面找*/
}
public void eat() {
System.out.println("老师吃饭!");
}
public void drank(){
System.out.println("老师喝水!");
}
}
测试结果:
3、构造方法的访问特点:
还是那句话,父类的构造方法不会被子类继承,但是可以被调用,另外,子类的所有构造方法默认先访问父类的构造方法中的无参构造,然后才执行自己,
使用super调用父类的构造方法,当用子类的构造方法创建(初始化)一个子类的对象时,子类的构造方法总是先调用父类的某个构造方法,也就是说,如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类的不带参数的构造方法。
由于子类不继承父类的构造方法,因此,子类在其构造方法中需使用 super 来调用父类的构造方法,而且super必须是子类构造方法中的头一条语句,即如果在子类的构造方法中,没有明显地写出super关键字来调用父类的某个构造方法,那么默认地有:
super();//调用父类的空参构造
super(参数1,参数2,~);//手动调用父类的有参构造
第一个是默认的,我们没有指明的话就是虚拟机会生成super(),并且在子类构造方法里面的第一行,放在其他位置就会报错,第二个是我们手动指明调用有参数的构造方法。
嘿嘿嘿,发现一个问题,就是父类里面有的方法,然后子类里面也有,方法名是一样的,这种情况叫什么呐-------->方法的重写
方法重写(Method Overriding)是面向对象编程中的一种特性,它允许子类重新定义父类中已经存在的方法,以便在子类中实现特定的功能,同时保留方法的名称和参数列表。
1、成员变量的隐藏:
成员变量的隐藏(Variable Hiding)是指在继承关系中,子类定义了与父类同名的成员变量,从而隐藏了父类中的同名成员变量。当子类中定义了与父类同名的成员变量时,子类对象访问该成员变量时将优先使用子类中的成员变量,而不是父类中的成员变量。这种行为称为成员变量的隐藏。
class Animal {
public int age = 5;
}
class Dog extends Animal {
public int age = 3;
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
Dog dog = new Dog();
System.out.println(animal.age); // 输出:5,访问父类中的成员变量
System.out.println(dog.age); // 输出:3,访问子类中的成员变量
}
}
成员变量的隐藏可以使得子类可以定义自己的成员变量,从而实现对父类成员变量的个性化定制。但是需要注意的是,成员变量的隐藏并不会改变继承关系,子类仍然可以访问父类的成员变量和方法,只是在访问同名成员变量时优先使用自己定义的成员变量。
2、重写的规则:
这里就是乱七八糟的,就是用简单的话来讲,重写的方法要和父类的保持一致就啥事没有,一致是指访问权限、方法名字、参数个数,参数类型,返回值类型。
子类通过方法方法的重写就可以隐藏继承父类的方法,通过重写可以父类的状态和行为转变成自己的状态和行为,举个例子!
如果父类的方法f()可以被子类继承,子类就有权重写 f(),一旦子类重写了父类的方法 f(),就隐藏了继承的方法 fo),那么子类对象调用方法 f()一定调用的是重写方法f();如果子类没有重写,而是继承了父类的方法 f(),那么子类创建的对象当然可以调用 f()方法,只不过方法 f()产生的行为和父类的相同而已。
重写方法既可以操作继承的成员变量、调用继承的方法,也可以操作子类新声明的成员变量、调用新定义的其他方法,但无法操作被子类隐藏的成员变量和方法。如果子类想使用被隐藏的方法或成员变量,必须使用关键字 super。