大学四年划水过去,现在到了找工作了才幡然悔悟,故从头开始学习Java,并针对自己之前不清楚的点做了笔记。视频课程是在b站上面找的,老师讲的生动有趣,十分推荐Java学习视频
Java内存划分
- 栈(Stack):存放方法中的局部变量(方法的参数或者方法{内部的变量})
- 方法的运行一定要在栈中运行
- 堆(heap):凡是new出来的东西都在heap中
- 堆内存里面的东西都有一个地址值:16进制
- 堆内存里面的数据都有默认值
- 方法区(Method Area):存储.class相关信息,包含方法的信息。
- 本地方法栈(Native Method Stack):与操作系统相关
- 寄存器(PC Register)
字符串常量池
程序当中直接写上的双引号字符串,就在字符串常量池中。(字符串常量池在堆中)
new的字符串对象不在池中,直接在堆中。
程序常量String str→字符串常量池中的字符串对象→底层的byte数组
"=="和"equal"的区别
- ==
- 对于基本类型来说,==是进行数值的比较
- 对于引用类型来说,==是进行【地址值】的比较
Static
关键字
- 如果成员变量使用static关键字,那么变量不再属于对象自己,而是属于所在的类,多个对象共享一份数据。如学号自增。
- 如果用static修饰成员方法,那么得到的静态方法不属于对象,而是属于类的。如果没有static关键字,那么必须先创建对象,才能通过对象使用。而使用static修饰的静态方法可以直接通过类调用
- 对于本类中的静态方法,类名称可以省略。
- 推荐使用"类名称.方法名"匿名调用,而不是"对象名.方法名"。后者会被编译为前者使用。前者可以直接看出是静态方法。
- 静态不能直接访问非静态,因为在内存中先有的静态,后有的非静态。
static内存图
静态代码块
Public class Person{
static {
System.out.println("静态代码块执行")
}
}
- 当第一次用到本类时,静态代码块执行唯一的一次。
- 静态内容总是优于非静态,所以静态代码块比构造方法先执行。
- 静态代码块的典型作用:用来一次性的对静态成员变量进行赋值。
继承
- 继承是多态的前提,没有继承就没有多态
- 继承主要解决的问题是:共性抽取
Java继承的三个特点
- Java是单继承的,一个类的直接父类只能有唯一一个。
- Java可以多级继承,更高级的父类也称为父类。
- 子类的直接父类是唯一的,但是一个父类可以有很多个子类。
继承中的重名访问
-
在父子继承关系中如果成员变量重名,则创建子类对象时,访问有两种方式
- 直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有就向上找。
- 间接通过成员方法访问成员变量:方法属于谁就优先用谁的,没有就向上找。
-
区分子类方法中重名的三种变量:
-
父子继承关系中,创建子类对象,访问成员方法的规则
- 创建的对象是谁,就优先用谁的,没有则往上找。
Override重写
概念:在继承关系当中,方法名称一样,参数列表也一样。
- 重写(Override):名称一样,参数列表也一样。覆盖,覆写。
- 重载(Overload):方法名称一样,参数不一样
注意事项:
- 必须保证名称参数相同,用
@Override
注解来检测正确。 - 子类方法的返回值必须小于等于父类方法的返回值范围。
- 子类方法权限必须大于等于父类方法的权限修饰符
- 权限从大到小:public > protected > (default) > private
使用意义:
- 设计原则:对于已经投入使用的类,尽量不要进行修改,推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容。(子类重写的方法可以调用父类方法→super.方法名())
父子类构造方法的访问特点
- 子类构造方法中有一个默认隐含的
super()
调用,所以一定是先调用的父类构造,后执行子类构造。 - 子类构造可以通过
super
关键字来调用父类重载构造。 super
的父类构造调用,必须是子类构造方法的第一句。不能一个子类构造调用多次super
构造。
总结:子类必须调用父类构造方法,不写则赠送
super()
;谢了则用写的指定的super
调用,super
只能有一个,还必须是第一句。
super
和this
关键字
super
关键字用来访问父类内容,而this关键字来访问本类内容
super
用法
- 在子类的成员方法中,访问父类的成员变量。
- 在子类的成员方法中,访问父类的成员方法。
- 在子类的构造方法中,访问父类的构造方法。
this
用法
- 在本类的成员方法中,访问本类的成员变量。
- 在本类的成员方法中,访问本类的另一个成员方法。
- 在本类的构造方法中,访问本类的另一个构造方法。
- this调用必须是构造方法的第一个语句,也是唯一一个。
- super和this两种构造调用,不能同时使用。
super
和this
关键字内存图解
抽象
如果父类当中的方法不确定如何进行{}方法体实现,那么这就应该是一个抽象方法。
- 抽象方法:加上abstract关键字,去掉大括号,直接分号结束。
- 抽象类:抽象方法所在的类,必须是抽象类才行,在class前面加上abstract。
如何使用抽象类和抽象方法:
- 不能直接new抽象类对象
- 必须用一个子类来继承抽象父类。
- 子类必须覆盖重写父类中所有的抽象方法。(除非子类也是抽象类)
- 创建子类对象来使用。
注:抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
接口
接口就是多个类的公共规范,是一种引用数据类型,最重要的内容就是其中的抽象方法。
如果是Java7,那么接口中可以包含的内容有:
1.常量
2.抽象方法
Java8增加的有
3.默认方法
4.静态方法
Java9增加的有
5.私有方法
接口里的抽象方法
注意事项:
- 接口当中的抽象方法,修饰必须是两个固定的关键字:
public
abstract
- 这两个关键字修饰符可以选择性的省略。
- 如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
接口使用步骤:
- 接口不能直接使用,必须有一个“实现类”来“实现”改接口(使用
implements
关键字修饰) - 接口的实现类必须覆盖重写接口中所有的抽象方法。
- 创建实现类的对象进行使用。
接口里的默认方法
格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
// 接口中的默认方法,可以解决接口升级的问题
//接口已有多个实现类时,若在接口中添加新的方法会导致已有的实现类因为未实现而报错,这时就可以使用默认方法
注意事项:
- 接口的默认方法可以通过接口实现类对象,直接调用。
- 接口的默认方法,也可以被接口实现类进行覆盖重写。
接口中的静态方法
从Java8开始,接口中允许定义静态方法。就是将abstract或者default换成static即可,带上方法体
注意:不能通过接口实现类的对象来调用接口中的静态方法。应该通过接口名称直接调用其中的静态方法。(子类只继承抽象方法)
接口中的私有方法
从Java9开始,接口中允许定义私有方案
问题描述:接口中需要抽取一个共同方法,用来解决两个默认方法之间重复代码的问题。但是这个共有方法不应该让实现类使用,应该是私有化的。
- 普通私有方法:解决多个默认方法直接重复代码的问题
private 返回值类型 方法名称(参数列表){
方法体
}
- 静态私有方法,解决多个静态方法之间重复代码段额问题
private static 返回值类型 方法名称(参数列表){
方法体
}
接口中的常量
- 接口中也可以定义“成员变量”,但是必须使用
public
static
final
三个关键字进行修饰。 - 接口当中的常量,必须进行赋值,不能不赋值。
- 接口中常量的名称,使用完全大写,单词之间使用下划线隔开。
接口之间的多继承
- 类与类直接是单继承的。直接父类只有一个。
- 类与接口之间是多实现的,一个类可以实现多个接口。
- 接口与接口之间是多继承的。
- 多个父接口当中的抽象方法如果重复,没关系。
- 多个父接口中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且带上
default
关键字。
接口内容注意总结
- 接口里面没有静态代码块或者构造方法。
- 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
- 如果实现类所实现的多个接口中有重名方法,那么只用实现一次。
- 如果实现类没有覆盖重写接口中所有的抽象方法,那么实现类就必须是一个抽象类。
- 如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类必须要对重名的默认方法进行重写。
- 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先使用父类中的方法。(优先级:父类>接口)
多态
extends
继承或者implement
实现是多态的前提
代码当中体现多态性,其实就是一句话:父类引用指向子类对象
父类名称 对象名 = new 子类名称();
接口名称 对象名 = new 实现类名称();
也就是子类对象被当做父类对象来使用
多态中访问成员变量的两种方法
- 直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有就向上找。
- 间接通过成员方法访问成员变量:方法属于谁就优先用谁的,没有就向上找。
多态中访问成员方法的规则
new的是谁就优先用谁的,没有就向上找。
总结:(多态、继承都适用)
- 成员变量:编译看左边,运行还看左边。
- 成员方法:编译看左边,运行看右边。
多态带来的好处
多态可以灵活调用派生类的属性方法
对象的向上向下转型
向上转型一定是安全的。但一旦向上转型,那么无法使用子类原本特有的内容。
向上转型→多态
向下转型→还原
final
关键字
final
关键字修饰类
含义:当前这个类不能有任何的子类。
- 一个类如果是
final
的,那么其中的方法无法被覆盖重写(因为没有子类)
final
关键字修饰方法
当final
关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写,也就是说对于一个类,不能同时被abstract
和final
关键字修饰,因为互相矛盾。
final
关键字修饰局部变量
一旦使用final
用来修饰局部变量,那么这个变量就不能进行改变。
- 对于基本类型来说,不可变说的是变量当中的数据不可改变。
- 对于引用类型来说,不可变说的是变量当中的地址值不可以改变,但是数据可以改变。
final
关键字修饰成员变量
对于成员变量来说,如果使用final
关键字修饰,那么这个变量也照样不可变。
- 由于成员变量具有默认值,所以用了
final
之后必须手动赋值,不会再给默认值了。 - 对于
fianl
的成员变量,要么使用直接赋值,要么通过构造方法赋值,二者选其一。 - 必须保证类当中重载的构造方法都要对
fianl
变量进行赋值。
四种权限修饰符
能否访问 | public | protected | (default) | private |
---|---|---|---|---|
同一个类 | YES | YES | YES | YES |
同一个包 | YES | YES | YES | NO |
不同包子类 | YES | YES | NO | NO |
不同包非子类 | YES | NO | NO | NO |
内部类
一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
成员内部类
一个类里面直接定义另一个类
注意: 内部类用外部类,随意访问;外部类用内部类需要借助内部类对象。
如何使用内部类
- 间接方式:在外部类的方法中,使用内部类;然后main只是调用外部类的方法。
- 直接方式:公式:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
内部类的同名变量访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dYMfniz1-1612178145245)(en-resource://database/635:1)]
内部类访问外部类的同名成员变量使用外部类.this.变量名
局部内部类
在一个方法内部定义一个类,那么这就是一个局部内部类
局部:只有当前所属的方法才能使用它,除了方法就不能用了。
注意:
- 局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效
fianal
的】new
出来的对象在堆内存中。- 局部变量是跟着方法走的,在栈内存当中。
- 方法运行结束后,立刻出栈,局部变量就会消失。
new
出来的对象会在堆内存中持续存在,直到垃圾回收消失。
匿名内部类
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】
匿名内部类的定义格式:
接口名称 对象名 = new 接口名称(){
//覆盖重写所有的抽象方法
}
匿名内部类使用注意事项:
- 匿名内部类在【创建对象】的时候,只能使用唯一的一次。
- 匿名对象在【调用方法】的时候,只能调用唯一的一次。如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
- 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】。
类的权限修饰符
定义一个类的时候,权限修饰符规则:
- 外部类:
public
/(default)
- 成员内部类:
public
/protected
/(default)
/private
- 局部内部类:什么都不能写