• 类和对象续


    目录

    自定义包

    包的访问权限控制

    常见的包

    Static成员

    静态成员变量

    静态成员方法

    代码块 

    构造块

    静态块

    重写 

    继承

    继承是啥?

    父类成员访问

    子类中访问父类成员变量

    两者不同名

    两者同名

    子类中访问父类对的成员方法

    super

    子类构造方法

    this和super

     代码块再解读

    protected关键字

    继承方式

     多态

    向上转型:把子类所引用的对象给到父类

    再说重写 

    向下转型

    多态的好处


    自定义包

    选中src右键选择new-->package,创建名字

    创建好名字后在下方新建一个testwww

    建完的界面就是这样滴

    包的访问权限控制

    在自定义包下创建新程序TestOne和一个新的包demo,在demo下再创建一个新程序TestTwo

    1. //TestOne
    2. package com.bitejiuyeke.www;
    3. public class TestOne {
    4. String name = "zhangsan";
    5. public static void main(String[] args) {
    6. }
    7. }

    因为封装在TestTwo中无法引用TestOne中的变量。

    常见的包

    1. java.lang: 系统常用基础类 (String Object), 此包从 JDK1.1 后自动导入。
    2. java.lang.reflflect:java 反射编程包 ;
    3. java.net: 进行网络编程开发包。
    4. java.sql: 进行数据库开发的支持包。
    5. java.util: java 提供的工具程序包。 ( 集合类等 ) 非常重要
    6. java.io:I/O 编程开发包。

    Static成员

    静态成员变量

     假设学生都在一个教室上课

    我们就把classroom拿出来当成静态类

    public static String classroom;

    特性:

    1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
    2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
    3. 类变量存储在方法区当中
    4. 生命周期伴随类的一生 ( 即:随类的加载而创建,随类的卸载而销毁 )

    静态成员方法

    1. public class Student{
    2. // ...
    3. private static String classRoom = "Bit306";
    4. // ...
    5. public static String getClassRoom(){
    6. return classRoom;
    7. }
    8. }
    9. public class TestStudent {
    10. public static void main(String[] args) {
    11. System.out.println(Student.getClassRoom());
    12. }
    13. }

     特性

    1. 不属于某个具体的对象,是类方法
    2. 可以通过对象调用,也可以通过类名 . 静态方法名 (...) 方式调用,更推荐使用后者
    3. 不能在静态方法中访问任何非静态成员变量

    初始化

    1.就地初始化

    直接赋值

    2.代码块初始化

    代码块 

    构造块

    静态块

    结论:

    静态代码块先执行

    然后执行构造代码块

    在实行对应的构造方法

    静态的只执行1次  

    同步代码块

    这些静态代码块可以合并成一个代码块


    重写 

    1. public static void main(String[] args) {
    2. Student student = new Student("张三",10);
    3. System.out.println(student);
    4. }

    打印结果是

    为什么会打印粗这样的结果呢?

    我们control点进去println底层

    valueOf底层

    toString底层

    我们完全可以再自己代码中重新写一个toString方法,这样系统不会调用底层的代码,而会先考虑你写的新方法

    点击鼠标右键选择generate,点进去toString,系统帮你生成一段新的打印代码,利用这段代码就可以正常打印了

    1. @Override
    2. public String toString() {
    3. return "Student{" +
    4. "name='" + name + '\'' +
    5. ", age=" + age +
    6. '}';
    7. //return "lalala"//这样就可以打印lalala
    8. }

    这种方法叫做重写


    java里面还有一个重写的亲兄弟:重载

    重载指在一个类中,具有多个相同名称的方法,他们的参数列表却不相同

    (参数类型不同、参数个数不同甚至是参数顺序不同

    1. //方法1
    2. public static int add(int a,int b) {
    3. return a+b;
    4. }
    5. //方法2
    6. public static int add(int a,int b,int c) {
    7. return a+b+c;
    8. }
    9. //方法3
    10. public static int add(int[] array) {
    11. int ret = 0;
    12. for (int x: array) {
    13. ret += x;
    14. }
    15. return ret;
    16. }
    17. public static void main(String[] args) {
    18. int[] array = {1, 2, 3, 4, 5};
    19. System.out.println(add(array));
    20. System.out.println(add(new int[]{1, 3, 4, 5, 6}));//匿名对象
    21. }

    方法3还有一个更🐂的重载方法

    1. /**
    2. * ...可变参数
    3. * @param array
    4. * @return
    5. */
    6. public static int add(int... array){
    7. int ret = 0;
    8. for (int x: array) {
    9. ret += x;
    10. }
    11. return ret;
    12. }

     


    继承

    继承是啥?

    这两个类都有相同的eat方法

    每个动物都有这几个共同的特点,把这些类的共性进行抽取,放到特定的类中,从而达到代码的复用效果。这种类就叫继承

    抽取新建共性类Animal

    1. class Animal{//抽取出来的类
    2. public String name;
    3. public int age;
    4. public void eat(){
    5. System.out.println(this.name + "正在吃饭");
    6. }
    7. }

     

    这里的Dog跟Animal是is-a的关系,Dog称为子类或者派生类,Animal称为父类或者基类或者超类

    extends是拓展的意思,也就是继承


    在Animal类中,访问修饰限定符只能决定访问权限,不能决定能不能被继承

    所以在后面的类中,虽然name不能被访问,但是是可以继承的,提供一个get方法就能访问


    父类成员访问

    子类中访问父类成员变量
    两者不同名
    1. class Base{
    2. public int a;
    3. public int b;
    4. }
    5. class Derived extends Base{
    6. public int c;
    7. public void method(){
    8. a = 1;
    9. b = 2;
    10. c = 3;
    11. }
    12. }
    13. public class Test2{
    14. public static void main(String[] args) {
    15. Derived derived = new Derived();
    16. }
    17. }

     这样的访问是不受限制的


    两者同名
    1. class Base{
    2. public int a = 9;
    3. public int b = 99;
    4. }
    5. class Derived extends Base{
    6. public int a = 88;
    7. public void method(){
    8. System.out.println("a: " + a);
    9. System.out.println("b: " + b);
    10. }
    11. }

    打印结果: 

    当父类和子类的成员变量同名时,在子类当中使用时,优先打印子类的成员变量

    总结:成员变量访问遵循就近原则,自己有优先自己的,没有再从父类中找


    子类中访问父类对的成员方法

    跟上面的成员变量访问差不多

    通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到
    则访问,否则编译报错。
    通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同 ( 重载 ) ,根据调用
    方法适传递的参数选择合适的方法访问,如果没有则报错;

    如果子类中存在和父类相同的成员时,怎么再子类中访问父类相同名称的成员呢?

    super

    super在子类方法中访问父类的成员变量和方法


    子类构造方法

    回到刚才的Animal类中,构造Animal类的构造方法

    Dog报错了,因为子类没有初始化父类成员,子类在构造完之前,一定要帮助父类进行初始化

    圆圆和10就是对name和age的初始化

    或者这样写(generate-->constructor)

    1. public Dog(String name, int age){
    2. super(name, age);
    3. }

    super()调用父类的构造方法,帮助初始化子类从父类继承过来的成员,并不会生成父类对象

    this和super
    相同点
    1. 都是 Java 中的关键字
    2. 只能在类的非静态(main不行)方法中使用,用来访问非静态成员方法和字段
    3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
    不同点
    1. this 是当前对象的引用,当前对象即调用实例方法的对象, super相当于是子类对象中从父类继承下来部分成员的引用
    2. 在非静态成员方法中, this 用来访问本类的方法和属性, super 用来访问父类继承下来的方法和属性
    3. 在构造方法中: this(...) 用于调用本类构造方法, super(...) 用于调用父类构造方法,两种调用不能同时在构造方法中出现
    4. 构造方法中一定会存在 super(...) 的调用,用户没有写编译器也会增加,但是 this(...) 用户不写则没有

     代码块再解读

    1. class Animal{//抽取出来的类
    2. public String name;
    3. public int age;
    4. public void eat(){
    5. System.out.println(this.name + "正在吃饭");
    6. }
    7. static {
    8. System.out.println("Animal::static{静态}");
    9. }
    10. {
    11. System.out.println("Animal::{实例}");
    12. }
    13. public Animal(String name, int age) {
    14. this.name = name;
    15. this.age = age;
    16. System.out.println("Animal(String name, int age)");
    17. }
    18. }
    19. //拓展
    20. class Dog extends Animal{//狗继承了Animal
    21. static{
    22. System.out.println("Dog::static{静态}");
    23. }
    24. {
    25. System.out.println("Dog::{实例}");
    26. }
    27. public Dog(String name, int age){
    28. super(name, age);
    29. System.out.println("Dog(String name, int age)");
    30. }
    31. public void bark() {
    32. System.out.println(this.name+ " 正在旺旺叫!");
    33. }
    34. }
    35. class Test{
    36. public static void main(String[] args) {
    37. Dog dog = new Dog("圆圆",10);
    38. }
    39. }

     打印结果是什么呢?

    两个静态优先执行,父类再执行,子类最后执行

    再多两行代码

    1. System.out.println("================");
    2. Dog dog2 = new Dog("圆圆",10);

    静态的就不执行了


    protected关键字

    1. package demo;
    2. public class Test3 {
    3. protected int a = 19999;
    4. }

    为什么不能打印a,就算调用super也不行呢?

    因为demo2里面的Test既不是Test3的子类,又与Test3处于不同包

    看回之前的那张表,这张表在我一篇博客提过

    JAVA类和对象_cx努力编程中的博客-CSDN博客

    此时protected正好满足第四类的限制

    这个表格的前提继承的父类是由public修饰的

    注意:这里的public不能替换成private或者protected


    继承方式

    1.现在有一需求:当集成到某个层次上之后我们不再继承了

    final是密封类,表示当前不能继承了。

    final的另一个用法。下面的a只能初始化一次,成为了常量,恒等于199,再次初始化会报错。

    1. final int a = 199;
    2. a = 20;//err
    3. System.out.println(a);

    final代表不可变 e.g String

    2.组合(has-a/a part of关系)

    1. class Tire{
    2. // ...
    3. }
    4. // 发动机类
    5. class Engine{
    6. // ...
    7. }
    8. // 车载系统类
    9. class VehicleSystem{
    10. // ...
    11. }
    12. class Car{
    13. private Tire tire; // 可以复用轮胎中的属性和方法
    14. private Engine engine; // 可以复用发动机中的属性和方法
    15. private VehicleSystem vs; // 可以复用车载系统中的属性和方法
    16. // ...
    17. }
    18. // 奔驰是汽车
    19. class Benz extend Car{
    20. // 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
    21. }

     多态

    同一个人对待不同人表现出不一样的形态,这可以理解为最基本的多态

    条件:

    1.继承关系上-->向上转型

    2.子类和父类有同名的覆盖/重写方法

    3.通过父类对象的引用去调用这个重写的方法


    向上转型:把子类所引用的对象给到父类

    常见的可以发生线上转型的三个时机 

    1. //1.直接赋值
    2. /*Dog dog = new Dog("圆圆",10);
    3. animal这个引用指向dog这个引用所指向的对象;
    4. Animal animal = dog;*/
    5. Animal animal = new Dog("圆圆",10);
    1. //2.方法的参数,传参的时候进行向上转型
    2. public static void func1(Animal animal){
    3. }
    4. public static void main(String[] args) {
    5. Dog dog = new Dog("圆圆",10);
    6. func1(dog);
    7. }
    1. //3.返回值向上转型
    2. public static Animal func2(){
    3. Dog dog = new Dog("圆圆",10);
    4. return dog;
    5. }

    再说重写 

    当我们在Dog类中加入eat()方法

    1. public void eat(){
    2. System.out.println(this.name + "正在吃狗粮!");
    3. }
    1. //Animal
    2. public void eat(){
    3. System.out.println(this.name + "正在吃饭");
    4. }
    5. //Test
    6. public static void main(String[] args) {
    7. Animal animal = new Dog("圆圆",10);
    8. animal.eat();
    9. }

    打印结果是

    这两个eat方法满足方法返回值一样,方法名一样,方法的参数列表一样 

    继承关系上,这两个方法关系是重写

    但是在调用的时候忽然变成了调用子类的eat,这个过程叫做动态绑定

    子类如果有eat方法那就调用子类的,子类没有就调用父类的

    程序在编译的时候,确实调用的是Animal的eat

    程序在运行时,调用了Dog的eat方法,是在这个时候才绑定方法(区分静态绑定)


    静态绑定是什么呢?

    像这种在编译的时候已经确定了调用谁,就是静态绑定


    我们在写重写时,一般要在上面加一个注释,这个注释可以帮助你避免一些错误

     实现重写:

    1.最基本的返回值,参数列表和方法名必须是一样的

    2.被重写的方法的访问修饰限定符在子类中要大于等于父类的

    访问修饰限定符大小关系:private>default>protected>public

    3.被private,static,final修饰的方法和构造方法是不能被重写的

    4.被重写的方法返回值类型可以不同,但必须有父子关系,比如:

    这种叫做协变类型 

    重写快捷方法,鼠标右键选generate,选择要重写的方法


    1. public static void eatFun(Animal animal){
    2. animal.eat();
    3. }
    4. public static void main(String[] args) {
    5. Dog dog = new Dog("圆圆", 19);
    6. eatFun(dog);
    7. Cat cat = new Cat("咪咪", 1);
    8. eatFun(cat);
    9. }
    10. //圆圆正在吃狗粮!
    11. //咪咪 正在吃猫粮!

    当父类引用的子类对象不一样的时候,调用这个重写的方法,所表现出来的行为是不一样的!我们把这种思想叫做多态

    向下转型

    我们知道dog和cat都属于animal,但是反过来,animal就一定是dog吗,这里拿一个大类去调用一个小类的方法明显不可能

    我们需要强制转换类型,这也叫做向下转型,但是这种转型非常不安全

    当我们使用cat类的时候就发现类型转换异常,就算强行转换也无济于事

    那我如何避免用错类来向下转型呢?我们可以用instanceof

    1. if (animal instanceof Cat) {
    2. Cat cat = (Cat) animal;
    3. cat.miaomiao();
    4. }else{
    5. System.out.println("好吧!");
    6. }

    多态的好处

    1. class Rect extends Shape{
    2. @Override
    3. public void draw(){
    4. System.out.println("矩形");
    5. }
    6. }
    7. class Triangle extends Shape{
    8. @Override
    9. public void draw() {
    10. System.out.println("🔺!");
    11. }
    12. }
    13. class Cycle extends Shape{
    14. @Override
    15. public void draw() {
    16. System.out.println("圆形!");
    17. }
    18. }
    19. public class Test {
    20. public static void main(String[] args) {
    21. Cycle cycle = new Cycle();
    22. Rect rect = new Rect();
    23. Triangle triangle = new Triangle();
    24. String[] strings = {"cycle","rect","cycle","rect","triangle"};
    25. for(String x :strings){
    26. if(x.equals("cycle")){
    27. cycle.draw();
    28. }else if (x.equals("rect")){
    29. rect.draw();
    30. }else if (x.equals("triangle")){
    31. triangle.draw();
    32. }
    33. }
    34. }
    35. }

    如果有大量的条件和循环语句,这种代码拥有圈复杂度,我们可以用多态来降低这种复杂度

    1. public static void drawMap(Shape shape){
    2. shape.draw();
    3. }
    4. public static void main(String[] args) {
    5. Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Triangle()};
    6. for(Shape shape: shapes){
    7. shape.draw();
    8. }
    9. }

    这段代码的扩展能力非常强,在上方添加一个类下面就能给你画出来

  • 相关阅读:
    JAVAAPI实现血缘关系Rest推送到DataHub V0.12.1版本
    统计一串字符中的字符种类个数
    员工离职困扰?来看AI如何解决,基于人力资源分析的 ML 模型构建全方案 ⛵
    Discourse 为什不建议使用 Gmail 的 SMTP
    网络安全(4)
    [航海协会]树
    商鼎云|亚马逊云盘关停了?你需要了解分布式存储云盘
    Redis的三种模式——主从复制、哨兵、集群
    MindSpore梯度进阶操作
    ES 中时间日期类型 “yyyy-MM-dd HHmmss” 的完全避坑指南
  • 原文地址:https://blog.csdn.net/hellg/article/details/132631913