继承

private
成员变量和方法,子类继承父类之后,虽然可以继承父类私有的数据,但是由于private访问修饰符的问题,子类没有办法直接访问该数据。这是因为继承主要影响的是行为(方法)而不是状态(数据)。所以在子类中访问父类被private修饰的成员时,会
编译出错。
父类成员访问
//创建Animal类(父类)
public class Animal {
public String name="小胖";;
public int age=5;
public void eat(){
System.out.println("去吃饭");
}
}
//创建dog类(子类)
class Dog extends Animal{
public String color="yellow";
//public String name="黑虎";//与父类中成员变量name同名
//与父类中成员方法eat()同名
// public void eat(){
// System.out.println("吃狗粮");
// }
public void method(){
//父类和子类成员同名时,根据就近原则,优先访问自己的成员变量
System.out.println(name);//访问从父类继承下来的name
System.out.println(age);//访问从父类继承下来的age
System.out.println(color);//访问子类自己的color
eat();//访问从父类继承下来eat()方法
//通过super关键字获取到子类从基类继承下来的部分
System.out.println(super.name);
super.eat();
}
}
//实现
class Test{
public static void main(String[] args) {
Dog dog=new Dog();
dog.method();
}
}
子类中访问父类的成员
1. 子类和父类不存在同名成员
super 关键字
作用:当子类和父类成员同名时,可以在子类方法中或者通过子类对象访问成员时,通过super关键字,优先访问父类的成员。
class Plant{
public Plant(){
System.out.println("这是Plant类的构造方法");
}
}
class Animal{
Plant plant = new Plant();//使用new关键字来实例化Plant类的对象
public Animal(){
System.out.println("这是父类的构造方法");
}
static {
System.out.println("这是父类静态代码块");
}
{
System.out.println("这是父类实例代码块");
}
}
class Dog extends Animal{
Plant plant = new Plant();//使用new关键字来实例化Plant类的对象
static {
System.out.println("这是子类静态代码块");
}
{
System.out.println("这是子类实例代码块");
}
public Dog() {
super();
System.out.println("这是子类构造方法");
}
}
public class Test {
public static void main(String[] args) {
Dog dog1=new Dog();
System.out.println("==================");
Dog dog2 =new Dog();
}
}
子类构造方法
super
关键字)来确保父类的成员变量得到正确初始化

protected 关键字
访问权限:
1. 同一包中的同一类
2. 同一包中的不同类
3. 不同包中的子类
package com.example;
public class Animal {
protected String name="黑虎";
}
package com.example2;
import com.example.Animal;
public class Dog extends Animal {
public void print(){
System.out.println(super.name);//super只能在非静态方法中使用
}
public static void main(String[] args) {
Dog dog=new Dog();
dog.print();
}
}
final关键字
final int age=10;
age=20;//err
final int[] arr1=new int[]{1,2,3,4,5};
arr1=new int[10];//err
不能修改arr1本身的内容即地址,即不能再指向另一个数组,但可以修改arr1指向的内容
final public class Animal{
...
}
class Dog extends Anmial{
...
}
//编译失败 java: 无法从最终com.example.Animal进行继承
//Animal被final修饰,不能被继承
组合
组合通常指的是**面向对象编程(OOP)**中的一种关系,即一个类包含另一个类的对象作为其成员变量。这种关系被称为“组合”或“has-a”关系,表示一个对象由其他对象组成。
组合的特点
-
has-a 关系:
-
一个类中包含另一个类的对象。
-
例如:
Car
类中包含Engine
类的对象,表示“汽车有一个引擎”。
-
-
强依赖关系:
-
组合中的成员对象通常与包含它的对象有强关联性。
-
如果包含对象被销毁,成员对象通常也会被销毁。
-
-
灵活性:
-
组合允许在运行时动态地替换成员对象。
-
例如:可以通过
setEngine()
方法更换汽车的引擎。
-
-
代码复用:
-
通过组合,可以复用其他类的功能,而不需要继承。
-
//轮胎类
class Tire{
public void print1(){
System.out.println("这是轮胎类");
}
}
//发动机类
class Engine{
public void print2(){
System.out.println("这是发动机类");
}
}
//组合
public class Car {
//tire和engine没有被初始化,它们默认为null
public Tire tire;//Car has-a Tire
public Engine engine;//Car has-a Engine
}
class Benz extends Car{
public void print3(){
tire.print1();
engine.print2();
}
}
class Test2{
public static void main(String[] args) {
Benz benz =new Benz();
//确保了在调用print3方法之前,tire和engine已经被正确初始化
benz.tire=new Tire();
benz.engine=new Engine();
benz.print3();
}
}
多态
在 java 中要实现多态,必须要满足如下几个条件,缺一不可:1. 必须在继承体系下2. 子类必须要对父类中方法进行重写3. 通过父类的引用调用重写的方法
重写:覆盖 覆写
需满足以下条件,缺一不可:
1. 方法名必须相同
2. 参数列表必须相同(类型,个数,顺序)
3. 子类中重写的方法必须返回与父类中被重写方法相同的类型,或者是其子类型(协变返回类型)。
方法重写的规则:
1. 被private修饰的方法不能被重写,因为该方法只能在当前类中被使用
2. 被static修饰的方法不能被重写,但可以被隐藏。如果子类有一个与父类静态方法同名的方法,它将隐藏父类的方法,而不是重写它。静态方法在编译时绑定,而重写方法在运行时绑定,
3. 被final修饰的方法不能被重写,此时这个方法叫密封方法
4. 子类中重写的方法不能拥有比父类访问权限更低的访问修饰符。private < default < protected < public
5. 构造方法不能被重写
6. 可以使用
@Override
注解来明确指出一个方法是重写了父类的方法。如果一个方法被标记为@Override
,但并没有重写父类的方法,编译器将报错。
class Anmial{
public String name;
public int age;
public String color;
public String eat(String name){
System.out.println("吃饭");
this.name=name;
return this.name;
}
}
class Dog extends Anmial{
public void run(){
System.out.println("用狗腿跑步");
}
//重写eat()方法
@Override
public String eat(String name){
System.out.println("吃狗粮");
this.color="yellow";
return this.color;
}
}
class Cat extends Anmial{
public void run(){
System.out.println("用猫腿跑步");
}
//重写eat()方法
@Override
public String eat(String name){
System.out.println("吃猫粮");
this.name=name;
return this.name;
}
}
重写与重载的区别:
区别点 | 重写 | 重载 |
---|---|---|
参数列表 | 不能修改 | 必须修改 |
返回类型 | 不能修改(除非可以构成父子关系) | 可以修改 |
访问限定符 | 子类中重写的方法不能拥有比父类访问权限更低的访问修饰符 | 可以修改 |
向上转型
向上转型和成员访问:
-
父类成员:向上转型后,可以访问父类中定义的所有成员变量和方法
-
子类重写方法:如果子类重写了父类的方法,通过父类引用调用时,实际上是调用的是子类中的实现。这是动态绑定或多态性的体现。
-
子类特有成员:不能直接访问子类中特有的成员变量和方法,因为这些成员在父类中不存在。

Anmial anmial = new Cat();//向上转型
// //实际写法
// Anmial anmial=new Anmial();
// anmial=new Cat();
System.out.println(anmial.eat("大胖"));//通过父类引用调用这个父类和子类重写的方法
2. 方法传参
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
print(cat);
}
//向上转型
public static void print(Anmial anmial){
System.out.println(anmial.eat("大胖"));
}
}
3. 方法返回
public class Test {
public static void main(String[] args) {
Anmial anmial = func();//向上转型
System.out.println(anmial.eat("大胖"));
}
public static Anmial func(){
return new Cat();
}
}
向下转型
将一个子类对象经过向上转型之后当成父类使用,但父类引用无法直接访问子类特有的成员变量和方法,如果需要访问子类特有的成员,必须进行向下转型(Downcasting)回子类类型。
进行转型:使用(子类类型)
将父类引用转型为子类引用。
instanceof
是一个关键字,它用于检查对象是否是特定类的实例。
instanceof
表达式返回一个布尔值,如果对象是指定类的实例,则返回true
;否则返回false。
instanceof
操作符通常用在需要确定对象的类型时,特别是在执行向下转型之前,需要确定对象实际类型,因为向下转型不安全。
public class Test {
public static void main(String[] args) {
Anmial anmial1 = new Cat();//向上转型
//向下转型前确定对象是否为Cat类型
if(anmial1 instanceof Cat){
Cat cat = (Cat) anmial1;//向下转型
cat.run();//访问子类特有的方法
}
}
}
什么叫 " 圈复杂度 " ?圈复杂度是一种描述一段代码复杂程度的方式 . 一段代码如果平铺直叙 , 那么就比较简单容易理解 . 而如果有很多的条件分支或者循环语句 , 就认为理解起来更复杂 .因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数 , 这个个数就称为 " 圈复杂度 ".如果一个方法的圈复杂度太高 , 就需要考虑重构 .不同公司对于代码的圈复杂度的规范不一样 . 一般不会超过 10
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
String[] shapes = {"吃狗粮", "吃猫粮", "吃狗粮", "吃狗粮"};
for (String shap : shapes) {
if (shap.equals("吃狗粮")) {
dog.eat("大黄");
} else if (shap.equals("吃猫粮")) {
cat.eat("大黄");
}
}
}
}
2. 使用多态
public class Test {
public static void main(String[] args) {
Anmial[] shapes={new Dog(),new Cat(),new Dog(), new Dog()};
for(Anmial shap:shapes){
shap.eat("大黄");
}
}
}
2. 可扩展能力更强
如果我们想要新增一种新的eat方法并打印, 使用多态的方式代码改动成本比较低,只要创建一个新的实例就可以了。而不使用多态的情况,就需要改动if...else 语句,成本更高
class Animal{
public Animal(){
eat();
}
public void eat(){
System.out.println("Anmial.eat()");
}
}
class Dog extends Animal{
public int age=10;
public Dog(){
super();
eat();
}
public void eat(){
System.out.println("Dog.eat()"+age);
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
}
}