【今日所学笔记】面向对象之封装、继承

1.类中成员变量访问权限

代表不能修饰类

访问修饰符同类中同包(子类和无关类)不同包(子类)不同包(无关类)
public☑️☑️☑️☑️
protected☑️☑️☑️✖️
default☑️☑️✖️✖️
private☑️✖️✖️✖️

几个问题:

  1. 在跨了包的子类里创建父类对象后,能不能通过此对象访问父类里protected成员?
    答:无法访问。只能自己创建子类对象来访问。
  2. 为什么不能用private修饰java外部类?
    答:首先语法上不允许,第二,如果用private修饰,那么不能创建对象实例,这个类的属性和方法不能被访问,那么毫无意义。

2.面向对象特征—封装

封装(Encapsulation)是指一种信息隐藏技术

  • 是指将数据和基于数据的操作封装在一起— 类定义

  • 数据被保护在类的内部 —通过访问权限

  • 目的:类的使用者class user 和设计者class creator分开

  • 优点:

    • 良好的封装能够减少耦合
    • 类内部的结构可以自由修改
    • 可以对成员变量进行精确的控制
    • 隐藏信息、实现细节

对于私有成员变量的访问,在所在类中提供两种方法:get() 、 set()

//形式如下
class A{	
		private int intPrivate;

    //注意,get()方法必须是public修饰的
    public int getIntPrivate(){
        return intPrivate;
    }
    //注意 set()方法也必须是public修饰,且不需要返回值
    public void setIntPrivate(int intPrivate){
        this.intPrivate = intPrivate;
    }
}

快捷键:mac/IDEA :command+n or conrtol+return

3.面向对象特征—继承

3.1继承是什么?

符合的关系:is-a
继承是 子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法。

⚠️注意:

  • 子类不能访问父类的私有成员
  • 子类不能继承父类的构造方法

关键字extendsimplements(暂时没学到后面再更新)
所有的类继承于 java.lang.Object ,当一个类没有上述关键字,则默认继承Object,祖先类。(这个类在java.lang包中,无需import来导包)

以下是多重继承://java不支持多继承

//子类可以继承父类以及祖先类的所有成员。
class GrandFatherClass{
  
}
class FatherClass extends GrandFatherClass{

}
class SonClass extends FatherClass{
  
}
//子类可以通过继承机制,不写任何额外代码就可以拥有父类的“所有”成员
//父类引用可以指向子类对象 
FatherClass Father = new SonClass(); //但是父类对象不能访问子类成员变量

3.2 为什么要继承?

实现类(成员)定义的复用,避免代码冗余

  • 优点:
    • 代码复用(类)
    • 提高了代码的可维护性
    • 弱化了Java中类型的约束(多态的前提条件之一)
  • 缺点:
    • 父类的所有修改都会反映在所有子类中 (所谓的牵一发而动全身?)
3.3 继承类型

贴个图:

3.4子类对象的初始化

继承了父类的子类,该如何初始化呢?

首先我门要知道一点:
子类对象的内存映像是怎样的?
在这里插入图片描述
由上图可知

  • 创建子类对象时,必须先加载子类的字节码文件到方法区;要加载子类,必须先加载父类。
  • 在子类对象中,有一块内存用于存储继承来自父类的成员变量值
    • 又,堆里的内存只存储对象的成员变量->故上述内存里的东西可以看成是一个父类对象

总结: 对于子类对象,内存里有两部分数据;
1. 子类继承来自父类的成员变量值
2. 子类自己定义的成员变量值

❗️核心:这两部分数据,初始化的先后顺序?
Java语言中,规定先父后子 ,因为子类的初始化有可能依赖于父类
例如:

class Father{
  int i;
}
class Son extends Father{
  int j;
  public Son(int j ){
    this.j = i*j;   //这种情况下必须先给父类的成员初始化
  }
}

那么问题来了,如何保证先父后子?
保证父类的构造方法先于子类的运行 -> 在子类的构造方法中,使用super关键字第一条语句的位置先调用父类构造方法。
一个例子:

public class Test{
  public static void main(String[] args){
    int sonI, fatherI;
    double sonJ , fatherJ;
    //把父类和子类的参数都赋上
    Son son = new Son(int sonI, int fatherI,double sonJ , double fatherJ);
  }
}

class Father{
  int fatherI;
  double fatherJ;
  
  public Father(int fatherI, double fatherJ){
    this.fatherI = fatherI;
    this.fatherJ = fatherJ;
  }
}

class Son{
  int sonI;
  double sonJ;
  public Son(int sonI,int fatherJ ,double sonJ,double fatherJ){
    //利用super关键字 显式 调用父类中定义的构造方法 
    //super(实参列表) 只要列表匹配即可 
    super( fatherI, fatherJ);
    this.sonI = sonI;
    this.sonJ = sonJ;
  }
}
3.5 super关键字

在一个类构造方法中this() 和 super()不能共存

this关键字super关键字
引用:表示当前对象的引用表示对父类对象的引用
访问:当前对象的成员变量值/方法父类对象中成员变量的值/方法
调用的构造方法:当前类中定义的父类中定义的构造方法
实参列表父类构造方法里的参数

最后:
今天的内容很多……而且每一个点摊开来都可以写一篇文章= =
尤其是每个修饰符可以修饰哪些东西 还有包内包外的不同……

还有父类子类调用来调用去的套娃以及构造方法的重载blabla……让人头晕

4.作业
  • 第一题
    关于静态代码块、构造代码块、构造函数
    问: 以下代码输出顺序?

class Base {
    static{
    	System.out.println("base static");
    }

    {
        System.out.println("base构造代码块");
    }

    public Base(){
        System.out.println("base构造函数");
    }
}

class Sub extends Base{
    static{
        System.out.println("sub static");
    }

    {
        System.out.println("sub构造代码块");
    }

    public Sub(){
        System.out.println("sub构造函数");
    }
}

public class Test03{
    public static void main(String[] args) {
        Sub sub = new Sub();
    }
}

结果:

 结果:
 base static
  sub static
  base构造代码块
  base构造函数
  sub构造代码块
  sub构造函数
 
  原因:
 * 1.在创建Sub类的对象过程中,首先执行类加载过程,JVM要认识子类,首先要认识父类,所以先加载父类,再加载子类
 * 2.static{}代码块会随着类的加载而先加载,先加载的父类所以父类的static{}先执行,子类的静态代码块后执行
 * 3.类加载过程完毕后,首先初始化父类对象,构造代码块先于构造函数执行(也可以说构造函数是在创建对象的最后一步执行的)
 * 4.然后初始化子类成员,在内存中,先有父类的那部分"对象"存在,所以先执行了父类的构造代码块和构造函数
 

第二题
问:定义一个Student类,并要求其他类在使用Student类的时候,最多只能创建10个Student类的对象,如何实现?(就是实现在一个jvm中,最多只能存在10个Student对象)

//首先要实现该功能,就不能让外部类使用Student类的所有构造方法,
//那么需要将权限改为private ;接着需要把创建对象的工作交给一个专门的方法
class Student {
    // 记录创建了几个对象,因为每次创建对象都需要修改这个值,故应该设为static
    private static int count =0;
    private Student(){
        System.out.println("private student"+count);
        count++; //每创建一次,count加一
    }

    //因为已经不能通过构造方法去创建对象,
    //所以只能用一个被static修饰的方法来创建对象并且返回对象的引用值
    //这样不需要创建对象就能访问此方法来创建……好绕
    public static Student createStudent(){
        //如果count小于10,则可以创建
        if(count<10){
            return new Student();
        }else{
        //如果超过十个,返回null
            System.out.println("超过10个");
            return null;
        }
    }
}
//测试类
public class Create{
    public static void main(String[] args) {
    //一个大于10次的循环来测试一下……
        for (int i = 0; i <18 ; i++) {
            Student student = Student.createStudent();
            if(student==null) break;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值