1.类中成员变量访问权限
代表不能修饰类
访问修饰符 | 同类中 | 同包(子类和无关类) | 不同包(子类) | 不同包(无关类) |
---|---|---|---|---|
public | ☑️ | ☑️ | ☑️ | ☑️ |
protected | ☑️ | ☑️ | ☑️ | ✖️ |
default | ☑️ | ☑️ | ✖️ | ✖️ |
private | ☑️ | ✖️ | ✖️ | ✖️ |
几个问题:
- 在跨了包的子类里创建父类对象后,能不能通过此对象访问父类里protected成员?
答:无法访问。只能自己创建子类对象来访问。 - 为什么不能用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
继承是 子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法。
⚠️注意:
- 子类不能访问父类的私有成员
- 子类不能继承父类的构造方法
关键字:extends
和implements
(暂时没学到后面再更新)
所有的类继承于 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;
}
}
}