6 Java的基本程序设计结构(基本语法5)- 面向对象进阶

news/2024/9/25 5:58:02/

文章目录

  • 面向对象进阶
  • 一、 static 静态
    • 1 静态变量
      • (1)基本定义和用法
      • (2)静态变量内存图
    • 2 静态方法
      • (1)基本定义和用法
      • (2)工具类
        • 练习:按下面需求写一个工具类
    • 3 static注意事项
    • 4 重新认识main方法
  • 二、继承
    • 1 继承的概念
    • 2 继承的特点
    • 3 继承到底能继承父类中的哪些内容?
    • 4 继承中的内存图
    • 5 继承中的语法特点(主要讲述继承中的语法)
      • (1)继承中:成员变量的访问调用
      • (2)继承中:成员方法的访问调用和子类对父类方法的重写
      • (3)构造方法在继承调用语法(采用继承中的方法重写手段)
        • 基本用法
        • 子类中的构造方法java虚拟机会自动先默认无参构造
      • (4)this 、super使用总结
    • 6 小练习:写一个完整的带继承关系的javabean类
  • 三、多态
    • 1 基本知识--概念+多态的两种参数声明方式(第二种才是重点,今后我们统一使用第二种方式)
    • 2 多态中多态声明传参方式2的强制转换
    • 3 判断对象是不是某一种类或者其子类:instanceof 运算符(限制只能在多态第二种传参调用开发中用:因为我发现第一种传参用这个运算符并列情况连编译都过不了,直接报错,非要用时记住这个并列会报错就可以了)
      • 语法
      • 使用场景
    • 4 一个多态项目练习(标志的多态书写案例)
  • 四、包和final关键字
    • 1 包
      • (1)什么是包
      • (2)java导包使用其他包里面的类
    • 2 final关键字
  • 五、权限修饰符和代码块
    • 1 权限修饰符
    • 2 代码块
  • 六、 抽象类和抽象方法
  • 七、 接口
    • 1 接口基本概念
    • 2 接口基本使用演示(成员变量、抽象方法)语法
    • 3 接口和类之间的关系
    • 4 接口的一般应用场景
    • 5 接口设计练习
    • 6 接口多学三招
      • (1)接口中的默认方法(jdk7以后才有的)
      • (2)接口中的静态方法(不重要)
      • (3)接口中的私有方法(不重要)
      • 小总结
    • 7 接口的多态
    • 8 适配器设计模式(编程套路:用来解决接口和实现类间矛盾的编程套路)
  • 八、 内部类
    • 1 成员内部类(了解即可)
      • (1)基本概念
      • (2)怎么获取成员内部类对象
      • (3)成员内部类如何获取外部类的成员变量(基本不可能用到)
      • (4)内部类内存图
    • 2 静态内部类(了解即可)
    • 3 局部内部类(了解即可)
    • 4 匿名内部类(重要!!!!,需要掌握,今后会经常用到)


面向对象进阶

这块是java编程基础!!!!


一、 static 静态

关于静态这个东西前面已经简单讲过,这里再详细讲一讲。

1 静态变量

(1)基本定义和用法

  • 定义:被static修饰的成员变量,叫做静态变量。
  • 特点:**被该类所有对象共享(简单来说你修改任意一个对象里面的static成员变量,所有对象的该成员变量的值都会变)
  • 调用方式**
    (1)直接类名调用(推荐)
    (2)对象名调用

关于被该类所有对象共享这个举个例子说明一下。
一个学生类,里面有一个静态成员变量是老师,修改任意一个学生对象的老师这个成员变量,其余学生对象的老师也会统一修改。简单来说,静态成员变量是属于这个类,是属于所有对象,修改一个对象就会影响所有对象。(现实中学生类一个班老师肯定都是一样的,就可以使用静态成员变量修饰)

java">package cn.hjblogs.demo;public class Student {private String name;private int age;public static String teacher_name;     // 静态变量// static String teacher_name;     // 不用public修饰就这样写就可以了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;}public static String getTeacher_name() {return teacher_name;}public static void setTeacher_name(String teacher_name) {Student.teacher_name = teacher_name;}public void show(){System.out.println("name: " + name + ", age: " + age + ", teacher_name: " + teacher_name);}
}
java">package cn.hjblogs.demo;public class Test {public static void main(String[] args) {Student stu1 = new Student("张三", 18);Student stu2 = new Student("李四", 20);stu1.show();                   // name: 张三, age: 18, teacher_name: nullstu2.show();                   // name: 李四, age: 20, teacher_name: nullstu1.setTeacher_name("张老师");stu1.show();                   // name: 张三, age: 18, teacher_name: 张老师stu2.show();                   // name: 李四, age: 20, teacher_name: 张老师//        Student.setTeacher_name("张老师");
//        stu1.show();                   // name: 张三, age: 18, teacher_name: 张老师
//        stu2.show();                   // name: 李四, age: 20, teacher_name: 张老师}
}

可以看到修改一个学生对象的老师其余学生对象的老师也变了。或者直接修改这个类的老师

(2)静态变量内存图

在这里插入图片描述

  • 静态变量是随着内的加载而加载的,优于对象出现在内存里面的。
  • 其实所有内存图都一样都是地址套地址,这里所有对象都拿着一个静态区里面静态变量的地址去找静态变量,这样静态变量还是个字符串,那又要拿着地址去串词里面找字符串(因为之前讲过字符串是存在串词里面的)所以这里面是一个地址套一个地址(一种比喻,反正肯定存在一个映射关系)

2 静态方法

(1)基本定义和用法

  • 定义:被static修饰的成员方法,叫做静态方法。
  • 特点:
    (1)多用在测试类和工具类中
    (2)javabean类中很少会用
  • 调用方式:
    (1)直接类名调用(推荐)
    (2)对象名调用

(2)工具类

工具类
目前我们已经学习了java中的三种类:

  • javabean类:用来描述一类事物的类,比如:Student、Dog、老婆
  • 测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
  • 工具类:不是用来描述一类事物的,而是帮我们做一些事情的类(里面,java.lang里面的Math类就是这样一个工具类,帮我们封装了一些数学函数)

书写工具类有一些注意事项

  • 类名见名知义
  • 构造方法私有化要,因为工具类创建对象没有任何意义,那么我们就将构造方法私有化直接不让创建对象就可以了
  • 方法定义为静态

下面的这个练习详细写了一个工具类的构造

练习:按下面需求写一个工具类

在这里插入图片描述

java">package cn.hjblogs.demo;public class ArrayUtil {private ArrayUtil() {}public static String printArray(int[] arr) {StringBuilder sb = new StringBuilder("[");for (int i = 0; i < arr.length; i++) {sb.append(arr[i]);if (i != arr.length - 1) {sb.append(", ");}}sb.append("]");return sb.toString();}public static double getAverage(double[] arr) {double sum = 0;for (double v : arr) {sum += v;}return sum / arr.length;}
}
java">package cn.hjblogs.demo;public class TestDemo {public static void main(String[] args) {int[] arr = {1, 2, 3, 4, 5};System.out.println(ArrayUtil.printArray(arr));             // [1, 2, 3, 4, 5]double[] arr2 = {1.1, 2.2, 3.3, 4.4, 5.5};System.out.println(ArrayUtil.getAverage(arr2));     // 3.3}
}

3 static注意事项

关键:静态只能访问静态,不能访问动态;动态可以访问所有

  • 静态方法只能访问静态变量和静态方法(静态方法中,只能访问静态)
    因为静态变量和方法是随着类的加载而加载的,优于对象出现在内存里面的。我们一般都会限定不能实例化,那么除了静态变量和方法其他的都没实例化,都没有进入内存,访问个屁。
  • 静态方法中是没有this关键字的
    前面讲this内存时将过this指向实例化的那个对象,但现在静态方法压根不需要实例化对象,this自然也就没有
  • 非静态方法可以访问所有

一句话:静态的东西随着类的加载而加载。当类被编译成字节码文件里面方法放进方法区,所有静态的就全部加载到内存(静态区里面去了),这里方法都没进栈呢,对象压根还没到创建的时候,静态的就早早进内存了,但非静态的东西是和对象有关的,对象都没创建进内存,访问个屁。
参考视频
在这里插入图片描述


4 重新认识main方法

在这里插入图片描述

  • public :被JVM调用,访问权限足够大
  • static:被JVM调用,静态,不用创建对象,直接类名访问
    因为main方法是静态的,所以测试类中其他方法也需要是静态的(因为静态只能访问静态,不能访问动态)
  • void:被JVM调用,不需要JVM返回值
  • main:一个通用的名称,虽然不是关键字,但是被JVM识别
  • String[] args:以前用于接收键盘录入数据的,现在没用; String[] 数组 args 变量名



二、继承

1 继承的概念

继续面向对象三大特性之一继承的学习
面向对象三大特性:封装、继承、多态

那么继承怎么理解呢?
在这里插入图片描述
继承:

  • java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系
  • 语法:public class Student extends Persons { }
  • Student称为子类(派生类),Persons称为父类(基类或超类)

使用继承的好处:

  • 可以把多个子类中重复的代码抽取到父类中了,提高了代码的复用性。
  • 子类可以在父类的基础上,增加其他功能,使子类更强大。

学好继承主要有两个点:

  • 自己设计
  • 能会用别人的

什么时候考虑用继承呢?
在这里插入图片描述

2 继承的特点

  • java只支持单继承,不支持多继承,但支持多层继承(A继承B,B继承C这种)。
  • 单继承:一个子类只能继承一个父类
  • 不支持多继承:子类不能同时继承多个父类
  • 支持多层继承(A继承B,B继承C这种):但这种关系有一个头的,java的设计中java的每一个类都间接或者直接的继承与Object这个总类。
  • 子类继承父类的所有属性和方法,你实例化子类但父类不一定实例化父类,所以你set改变了子类中的东西,父类是没有任何变化的,因为父类都没有实例化。所以如果你对继承有疑惑,修改子类对象东西会影响到父类吗?答案是你修改子类,父类都不一定实例化了,根本没有任何影响。但是反过来父类是影响子类的,
    (1)修改子类不会影响父类
    (3)父类发生变化会影响子类
    (3)子类继承父类的几乎所有属性和方法(具体能继承哪些,后面有一节会详细说明,另外还一般是非私有的才能继承),可以直接 子类.属性或者子类.父类方法调用

    比如 public class Student { } 如果我们没有写继承,但java内部这个类默认是会直接继承Object这个类的。

【注】:java不支持多继承,但是Python和C++支持多继承。

下面来一个练习,看是不是真的理解了
在这里插入图片描述
先看一下结构
在这里插入图片描述

java">package cn.hjblogs.Animal;public class Animal {public Animal() {}public void eat(){System.out.println("吃饭");}public void drink(){System.out.println("喝水");}
}
java">package cn.hjblogs.Animal;
public class Cat extends Animal {public Cat() {}public void catchMouse() {System.out.println("抓老鼠");}
}
java">package cn.hjblogs.Animal;
public class Dog extends Animal {public Dog() {}public void seeDoor() {System.out.println("看家");}
}
java">package cn.hjblogs.Animal;
public class DragenLiCat extends Cat {public DragenLiCat() {}
}
java">package cn.hjblogs.Animal;
public class RagdollCat extends Cat{public RagdollCat() {}
}
java">package cn.hjblogs.Animal;
public class HuskyDog {public HuskyDog() {}public void downHome() {System.out.println("拆家");}
}
java">package cn.hjblogs.Animal;
public class TeddyDog extends Dog {public TeddyDog() {}public void wipe(){System.out.println("蹭一蹭");}
}
java">package cn.hjblogs.Animal;public class Test {public static void main(String[] args) {// 创建对象RagdollCat a = new RagdollCat();// 调用方法a.eat();         // 吃饭a.drink();       // 喝水 a.catchMouse();   // 抓老鼠}
}

3 继承到底能继承父类中的哪些内容?

先说结论吧,能继承的东西无非就是 构造方法、成员变量、成员方法中考虑就是了

内容非私有私有
构造方法不能不能
成员变量
成员方法(只有虚方法才能被继承)不能
内容虚方法(非private、static、final修饰的方法)非虚方法
成员方法(只有虚方法才能被继承)不能

其实在不同语言中基本上私有的都继承不了
1、java这里面有个特例,但没关系,私有就算能继承但由于是私有继承了子类也访问不了,这个继承了和没继承效果是一样的。(对于继承下来的私有变量非要用就只能通过get和set方法进行拿和修改了,前提是父类中的get和set不是私有,要是也是私有那完了,彻底访问不了了)
2、所以我们记住,私有的不能被继承(不是很严谨,但是够用)

这三个里面比较复杂的就是构造方法的问题了,这里面涉及到了方法的重写和super调用。具体的语法详情我们在(5)继承中的语法中会详细演示



4 继承中的内存图

参考视频

成员变量
在这里插入图片描述
和之前主要有两个不同之处

  • public class Zi extends Fu{}: 加载字节码文件时同时会将父类的字节码文件一起存到方法区中
  • new 创建对象在堆中一个地址分成了两块,一块存父的所有属性和方法,另一块存子独有的

在这里插入图片描述

成员方法
开始这个之前先要了解一下java中的虚方法表,java在继承中调用方法并不是一级一级不断往上找,因为这样如果继承链过长,会导致效率过慢。java的设计解决方法是从最上层每个类将自己的虚方法(能被继承方法)放进自己的虚方法表,子类继承这个表再在里面添加自己的虚方法,这样每个子类调用方法去自己的虚方法表里面找效率一下就提高了,不是虚方法表再回自己独有的去找,找不到就报错。
在这里插入图片描述

在这里插入图片描述

5 继承中的语法特点(主要讲述继承中的语法)

(1)继承中:成员变量的访问调用

成员变量的访问特点很简单就一句话:就近原则,先在本类中就近原则(先在方法内部找,方法内部找不到去类里面成员变量区找),本类找不到(成员变量区都找不到)再去父类中找。
下面这个例子就是典型的就近原则,但是现实中写代码不可能这么写啊(妥妥的超级危险代码),这里演示就近原则才举一个这么极端的例子:
在这里插入图片描述
小练习:

java">package cn.hjblogs.Business;public class Test {public static void main(String[] args) {// 创建对象Zi z = new Zi();}
}class Fu{String name = "Fu";String hobby = "喝茶";
}class Zi extends Fu{String name = "Zi";              // 子类继承属性,属性是可以重写的属性String game = "吃鸡";public Zi(){//如何打印ZiSystem.out.println(name);          // Zi(就近原则)System.out.println(this.name);     // Zi//如何打印FuSystem.out.println(super.name);    // Fu//如何打印喝茶System.out.println(hobby);         // 喝茶(就近原则)System.out.println(this.hobby);    // 喝茶(继承父类属性,并且没有重写)System.out.println(super.hobby);   // 喝茶//如何打印吃鸡System.out.println(game);          // 吃鸡(就近原则System.out.println(this.game);     // 吃鸡}
}

可以看到就近原则的存在,使得用法很灵活,但就近原则这个东西虽然java给你做了一层就近原则的防护,但这是危险代码的事实不会改变,因此我们平时应该养成习惯,就近原则尽量不要使用,用this、和super不香吗,没必要折腾给代码增加风险。

总结一下就近原则:

  • (1)直接变量名访问,先在调用的方法的局部找,找不到再到类的成员变量区找,还找不到就只能到父类里面去找了
  • (2)使用this访问,直接到类的成员变量区去找(并且如果类继承了父类,直接this也能找到对应属性;理解成this的成员变量区有部分属性是隐藏的,带上this还是能够访问到)。
  • (3)使用super访问:直接到父类去找了。

(2)继承中:成员方法的访问调用和子类对父类方法的重写

成员方法的调用还是一样的规则,使用就近原则。(所以子类对父类方法的重写就是典型的调用案例)
结合下面的直接调用,完全重写和部分重新的代码,一下子就清晰了

java">package cn.hjblogs.Business;public class Test {public static void main(String[] args) {// 创建对象ChinesePerson a = new ChinesePerson();// 调用方法a.lunch();         // 吃饭 喝水 吃饭 喝水System.out.println("=====================================");ForeignPerson b = new ForeignPerson();b.lunch();         // 外国人吃牛排 喝水 外国人吃牛排 喝水 吃饭 喝水}
}class Person {public void eat() {System.out.println("吃饭");}public void drink() {System.out.println("喝水");}
}// 演示继承中方法调用的优先级 this和super
class ChinesePerson extends Person {public void lunch(){// this这里方法调用同样是就近原则,先调用子类的方法,如果子类没有再调用父类的方法this.eat();this.drink();// 直接调用父类的方法super.eat();super.drink();}
}// 演示继承中方法的重写的两种方式 1.完全重写 2.部分重写(在父类的部分上进行修改  一般结合super)class ForeignPerson extends Person {// 完全重写public void eat(String a) {System.out.println(a);   // 外国人吃牛排}// 部分重写(在父类的部分上进行修改  一般结合super)@Overridepublic void drink() {super.drink();  // 调用父类的方法System.out.println("外国人喝红酒");}public void lunch(){// this这里方法调用同样是就近原则,先调用子类的方法,如果子类没有再调用父类的方法String food = "外国人吃牛排";this.eat(food);         // 外国人吃牛排this.drink();       // 喝水 外国人喝红酒// 直接调用父类的方法super.eat();         // 吃饭super.drink();       // 喝水}
}

【注】:关于方法继承重写,添加新的方法参数没有任何关系,如果要用super调用父类中的方法,该有的参数就都要传全才行

另外关于方法重写在java中还有一些语法规范:

  • 当父类的方法不能满足子类现在的需求时,需要进行方法重写
  • 书写格式:函数名一样即可(严格来说就是子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法)。java中重写方法的名称、形参列表必须与父类中的一致。(不一致就不是方法的重写了,叫做创建了一个新方法)。这点来看Python要比java灵活多了。
  • @Override重写注释(不加也没任何关系)
    (1)@Override是放在重写后的方法上,校检子类重写时语法是否正确。
    (2)加上注解后如果有红色波浪线,表示语法错误。
    (3)建议重写方法都加@Override注解,代码安全,优雅!但是我个人觉得不加这个注解会更加灵活好用,下面是解释,反正我以后没有特殊规定是不会写的。

另外,在java中@Override认为如果方法重写过程中参数变多,或者参数顺序变量,那么@Override认为这就不是方法的重写,这是一个新的属于子类的方法。(结合前面的方法重载来解释,好像java中的确是将参数不同,顺序不同的同名函数不是看成一个函数,而是多个函数),这是一个小细节。
Python中就没这些规定了,语言不同,一些细微差别还是有的,所以建议@Override注解如果没有强制要求,可以不写,加了@Override注解反而丧失了灵活性,代码给自己看的,何必管别人。
我喜欢将java中的这种方法重写叫做狭义的方法重写,Python中的那种更加灵活叫做广义的方法重写。java中想实现广义的方法重写也简单,不要加@Override注解就行了,参数顺序随便变,随便加参数,新方法就新方法呗,够灵活就可以了。

  • 子类重写父类方法时,访问权限子类必须大于等于父类(空着不写<protected<public),通常都是public,public,所有权限都给你
  • 子类重写父类方法时,返回值类型必须小于等于父类(不重要,针对狗类里面有方法返回动物类这种离谱现象的操作,子类里面有个方法的返回值是父类,很扯)
  • 只有被添加到虚方法表(非private、static、final修饰的方法)中的方法才可以被重写。

最后,不要将眼光限定在java一种规则上,如果其他语言的规则更好用,我们为什么不直接使用那种规则,更加灵活当然更好啊。


(3)构造方法在继承调用语法(采用继承中的方法重写手段)

基本用法
  • java中构造方法不能继承很显然,毕竟子类和父类构造方法不可能重名,这种涉及天然导致java中的构造方法不能被继承。因此在子类中只能进行构造方法的重写。
  • Python中的构造方法_init_如果在子类中没有被显式写出,那么Python会默认继承父类的_init_方法;但是我们写Python类怎么可能不写_init_方法,因此,还是要进行_init_方法的重写

在子类中进行构造方法的重写,有分先后顺序必须要干的三件事情(严格按照顺序)

  • 第一件事情就是父类中构造方法传入的参数,子类的构造方法也必须要传入
  • 第二件事情:用super调用父类的构造方法把继承来的父类属性初始化了
  • 第三件事情:初始化子类其他的属性或者其他操作

看下面代码示例就很好理解了,java和Python版都有:

【注】:java可以一个文件里面写多个类(但是只有一个类能被public修饰),这里为了好讲述才这样写一下,实际java开发中还是要一个类一个文件夹. 算了还是分多个文件class避免误解吧!

java

java">public class Parent {int value;Parent(int value) {this.value = value;}
}
java">public class Child extends Parent {int childValue;Child(int value, int childValue) {

http://www.ppmy.cn/news/1502147.html

相关文章

视图,存储过程和触发器

目录 视图 创建视图&#xff1a; 视图的使用 查看库中所有的视图 删除视图 视图的作用&#xff1a; 存储过程&#xff1a; 为什么使用存储过程&#xff1f; 什么是存储过程&#xff1f; 存储过程的创建 创建一个最简单的存储过程 使用存储过程 删除存储过程 带参的存储…

企业获客重要途径-大数据获客系统

企业获客的重要途径之一是通过大数据获客系统。这一系统利用大数据技术和分析方法&#xff0c;帮助企业更精准地获取客户&#xff0c;提高市场营销的效率和效果。 所以整理了以下是大数据获客系统作为企业获客重要途径的详细阐述&#xff1a; 一、大数据获客系统的定义与功能…

常见CSS属性(二)——浮动

一、浮动简述 浏览器在解析html文档时&#xff0c;正常的顺序是从上往下、从左往右解析。这个正常的解析过程&#xff0c;叫做正常文档流(标准文档流)&#xff0c;而浮动就是使得元素脱离文档流&#xff0c;“浮”在浏览器上。 浮动会使元素脱离文档流&#xff0c;不占位置&…

【python】OpenCV—Faster Video File FPS

文章目录 1、需求描述2、正常方法 cv2.read3、加速方法 imutils.video.FileVideoStream4、涉及到的核心库函数4.1、imutils.video.FPS4.2、imutils.video.FileVideoStream 5、参考 1、需求描述 使用线程和队列数据结构将视频文件的 FPS 速率提高 &#xff01; 我们的目标是将…

Meta Llama 3.1:AI领域的新里程碑

Meta最近推出了其最新的AI模型Llama 3.1&#xff0c;这不仅是一个技术上的飞跃&#xff0c;更是AI领域的一次重大突破。以下是Llama 3.1的一些关键技术亮点&#xff0c;值得每一位AI爱好者和开发者关注。 参数规模与性能 Llama 3.1包含三种规格&#xff1a;8B&#xff08;80亿…

Linux并发程序设计(5):线程基础

目录 一、介绍 1.1 线程与进程的区别 1.2 线程特点 1.3 线程共享资源 1.4 线程私有资源 1.5 Linux线程库 二、相关函数 2.1 线程创建 – pthread_create 2.2 线程结束 – pthread_exit 2.3 线程查看tid函数 2.4 线程回收 2.4.1 使用pyhread_join进行线程回收 2.4.…

前端怎么本地起一个服务查看本地文件

1.安装拓展 安装 Live Server拓展 2.创建一个html文件 3.在html文件中右键选择 Open with Live Server 4.浏览器打开运行的地址&#xff0c;并去除路径&#xff0c;例如:http://127.0.0.1:5500/

20240725java的Controller、DAO、DO、Mapper、Service层、反射、AOP注解等内容的学习

在Java开发中&#xff0c;‌controller、‌dao、‌do、‌mapper等概念通常与MVC&#xff08;‌Model-View-Controller&#xff09;‌架构和分层设计相关。‌这些概念各自承担着不同的职责&#xff0c;‌共同协作以构建和运行一个应用程序。‌以下是这些概念的解释&#xff1a;‌…