文章目录
- Java常用关键字学习
- 1、static关键字学习
- 1.1 用法一:修饰成员变量
- 1.2 用法二:修饰成员方法
- 1.3 用法三:修饰代码块
- 1.4 用法四:修饰内部类类
- 1.5 单例设计模式
- 2、extends关键字学习
- 2.1 继承的特点
- 2.2 方法重写
- 3、this、super关键字学习
- 3.1 this和super基本用法和注意事项
- 3.2 一个"简单"的面试题:
- 4、final关键字学习
- 4.1 用法一:修饰变量
- 4.3 用法三:修饰类
- 5、enmu关键字学习
- 6、abstract关键字学习
- 6.1 用法二:修饰方法
- 6.2 用法一:修饰类
- 6.3 模板方法设计模式
- 7、interface关键字学习
- 7.1 接口的特点和用法
- 7.2 implements关键字学习
- 7.3 接口和多态的综合使用案例
- 结语
Java常用关键字学习
推荐阅读:👉JavaSE基础回顾
1、static关键字学习
static
在英文中是” 静止的 静止的 静止的“,正如其英文含义,在Java中用static关键字修饰的成员变量、成员方法、代码块、内部类,它们会被存储在一个固定大小的内存区域,就像是”静止在内存中“一样,当执行 程序时,能够快速的访问它们。
被static修饰具有的特点:
- 数据共享。属于类级别,不需要创建对象就可以直接使用,是全局作用域
- 全局唯一。在内存中存储固定且唯一(如果修饰成员变量,变量名具有唯一性)
- 自动初始化。当类被加载的时候,就自动加载到内存中(只会被加载一次)
使用static修饰的优点和缺点:
- 优点:
- 对于频繁使用的数据使用static修饰能够节约内存。原因:被static修饰的数据只加载到内存中一次,不需要再去new对象,每次new一个对象都是需要开辟一个新的内存空间的(通常用来创建工具类,工具类构造器私有化显得很专业)
- 能够提高程序的效率。原因:static修饰的数据在内存中的存储位置固定且唯一,能够很便捷被JVM访问
- 缺点:
- 大量使用会占用内存。原因:静态变量的生存周期和所在类相同,会长时间占用内存,所以不宜大量使用
- 存在线程安全问题。原因:static数据能够共享(一但数据存在共享,一般都会出现线程、并发等安全问题)
1.1 用法一:修饰成员变量
static修饰成员变量:静态变量 既可以被对象调用(不推荐),又可以被类调用
-
静态变量被其他类访问时,可以直接通过 类名 . 变量 类名.变量 类名.变量名进行访问。原因:静态变量存储位置固定,JVM能够直接通过类名找到静态变量的存储地址
-
而在自己所在类中,可以被直接访问。原因:当类被加载时,静态变量会自动加载到内存中,导致静态变量在类中具有全局作用域,而普通变量是只有当对象实例化后才会加载到内存中
备注:static只能用于修饰成员变量1,不能用来修饰局部变量2
static还能用于修饰集合:
public static ArrayList<String> cards = new ArrayList<>();
1.2 用法二:修饰成员方法
static修饰成员方法:静态方法(也叫类方法) 只能被静态方法调用
- 静态方法只能访问static修饰的成员3。原因:没有static修饰的成员需要先实例化对象,通过对象来调用成员
- 使用static的静态方法不能以任何方式引用this和super关键字。原因:静态方法被调用时不产生任何对象,this、super找不到引用的对象
class Student{
public void say(){
System.out.println("hi!我是普通方法");
}
public static void speak(){
System.out.println("hi!我是静态方法");
}
public static void main(String[] args) {
//调用普通方法
Student s = new Student();
s.say();
//调用静态方法
speak();
// s.speak();也可以使用对象调用,但不推荐
}
}
1.3 用法三:修饰代码块
static修饰代码块:静态代码块
- 类被加载,静态代码块就被执行,且只会被执行一次。
静态代码块一般用于提前初始化数据,比如:斗地主牌的准备就可以放在一个专门的静态代码块中
备注:类中的代码块和构造器一样,一但类被实例一次就会被加载一次
1.4 用法四:修饰内部类类
用static修饰的内部类叫做
静态内部类
它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已
特点:
- 静态内部类不能访问外部类的实例成员(可以使用外部类的实例对象来访问)
- 静态内部类的静态方法能访问外部类的静态方法
- 静态内部类能访问外部类中的静态成员变量
注意事项:Java是不能直接在类中对变量进行赋值的,只能在定义的时候赋值,或者在方法中赋值,或者在代码块中赋值
语法:
public class Outer{
// 静态成员内部类
public static class Inner{
……;
}
}
//示例化对象
外部类名.内部类名 对象名 = new 外部类名.内部类构造器;
1.5 单例设计模式
设计模式:开发中经常遇到一些问题,一个问题通常有n种解法的,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
学设计模式主要是学2点:
第一:这种模式用来解决什么问题。
第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
单例模式:保证系统中,应用该模式的这个类永远只有一个实例,即:一个类永远只能创建一个对象
单例模式的优点:
- 节约内存。无论实例化多少次对象,只加载到内粗中一次
- 保证实例对象的唯一性(有些情况必须要一个唯一的实例对象,否则系统就会混乱,比如:任务管理器的设计就必须使用单例模式)
饿汉单例模式:在用类创建对象时,对象已经提前为你创建好了
(通过类名.静态变量
获取对象)
- Step1:定义一个类,将构造器私有化
- Step2:定义一个静态变量存储new创建的对象
//饿汉单例模式
public class SingleInstance {
//stept2:提前准备对象,该对象只加载一次,具有唯一性
public static SingleInstance si = new SingleInstance();
//step1:构造器私有化
private SingleInstance(){
System.out.println("无论你实例化多少次si对象,我都只运行一次");
}
}
class test{
public static void main(String[] args) {
SingleInstanceDemo1 s1 = SingleInstanceDemo1.si;
SingleInstanceDemo1 s2 = SingleInstanceDemo1.si;
System.out.println(s1==s2);//true
}
}
懒汉单例模式:在真正需要该对象的时候,才去创建一个对象(延迟加载对象)
(通过)
- Step1:定义一个类,将构造器私有化
- Step2:定义一个静态变量存储一个对象(一定要私有化)
- Step3:提供一个返回单例对象的方法
//懒汉单例模式
public class SingleInstanceDemo2 {
//Step2:定义一个静态变量,用来存new创建的对象。不私有化会让外界动过类点对象访问到,但是此时很有可能为null
private static SingleInstanceDemo2 si;
//Step3:定义一个返回单例对象的方法
public static SingleInstanceDemo2 getInstance(){
if(si == null){
si = new SingleInstanceDemo2();
}
return si;
}
//Step1:私有化构造器
private SingleInstanceDemo2(){
System.out.println("无论你实例化多少次,我都只会被执行一次");
}
}
class test2{
public static void main(String[] args) {
/*System.out.println(SingleInstanceDemo2.si);
//如果不加private,能够访问到si,但是很可能为null。加private直接让外界访问不到,防止出bug*/
SingleInstanceDemo2 si1 = SingleInstanceDemo2.getInstance();
SingleInstanceDemo2 si2 = SingleInstanceDemo2.getInstance();
System.out.println(si1==si2);//true
}
}
两种模式的比较:
-
相同点:都是解决一个类只能存在一个对象的问题,都需要将构造器私有化、都需要利用static关键字实现功能
-
不同点:
1)懒汉比饿汉节省内存。懒汉是提前实例化对象,将数据加载到内存中国,无论是否需要都占用了内存,而懒汉是用的时候才加载到内存中,能更好的节约内存
2)饿汉比懒汉节省时间。饿汉是提前将数据加载到内存中的,随时可以访问;同时饿汉获取对象还需要进行判断
🐟与🐻不可兼得啊
推荐阅读:一文搞懂static
2、extends关键字学习
2.1 继承的特点
继承的好处:提高代码的复用性,减少代码冗余,增强类的扩展性
继承的特点:
- 子类可以继承父类的属性和方法,但子类(派生类)无法继承父类的构造器
- Java是单继承模式:一个类只能继承一个直接的父类(超类、基类、根类)
- Java不支持多继承,但是支持多层继承
- Object是所有类的父类(一切皆对象)
备注:
- 子类无法继承父类private修饰的成员(本质上是继承了,只是继承了也不能访问)
- 继承的子类可以直接通过自己的实例对象使用父类static修饰的成员,但并非继承而是共享
子类访问成员遵循就近原则: 子类局部范围找 → 子类的全局范围找 → 父类的全局范围找 → 父类的父类找 → … … 子类局部范围找\rightarrow{子类的全局范围找}\rightarrow{父类的全局范围找}\rightarrow{父类的父类找}\rightarrow{……} 子类局部范围找→子类的全局范围找→父类的全局范围找→父类的父类找→……
继承后子类构造器的特点:继承后的子类每次执行自己的任意一个构造器会默认优先访问执行父类的无参构造器,再执行自己
原因:子类继承了父类,需要用到父类中的数据,如果父类没有完成初始化(初始化就进入了内存),子类就无法去使用父类的数据
子类构造器都会默认存在一个
super()
方法如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?
会报错。因为子类默认是调用父类无参构造器的。
经验:一般所有的类都会默认存在一个物产构造器,但一旦我们设置一个有参构造器时,就会自动消失,所以当我们开发时,给一个类加一个有参构造器时,就需要手动设置一个无参构造器,养成规范,避免出现bug
2.2 方法重写
只有当一个类继承另一个类后才能重写,即:子类重写继承了父类的方法
重写注解:@Override
- 告诉编译器这是一个重写的方法,可以检测重写是否符合规范
- 提高代码的可读性,让代码更加规范(别人一看就知道你这是一个重写的方法)
重写注意事项:
- 重写方法的名称、形参列表必须与被重写的方法一致(重写只是更改了方法的内部结构,并没有改变它的外部结构,简言:
声明不变,重新实现
) - 子类重写父类方法时,访问权限必须≥父类(不能重写父类private修饰的方法,省略权限关键字(default)<protected<public)
- 子类不能重写父类的静态方法(static修饰的成员并不会被继承)
3、this、super关键字学习
3.1 this和super基本用法和注意事项
this和super的用法:
this和super注意事项:
-
this(……)、super(……)中的参数列表与待访问的构造器参数列表一致
-
this和super关键字不能共存于同一个构造器中
注意事项:
使用this访问构造器时需要注意:
- 只能再构造方法中使用this访问构造器
- 在构造方法中使用this访问构造器的语句必须写在第一条
- 同一个类中的两个构造方法使用this相互调用(会报
Receusive constructor invocation Person()
错误提示信息)
使用super访问构造器时需要注意:
- 所有类中的无参构造方法都会在第一行默认存在一个
super()
方法 - 由于1的存在,最好在每个类中都显示地定义一个无参构造方法
3.2 一个"简单"的面试题:
考察的知识点:
-
new需要干啥
-
构造器默认存在super()方法
-
等号赋值的特点
-
方法的调用由对象的类型决定
快来看看你得到的是答案属于哪一阶段吧😄:
小白阶段(懂1):20
入门阶段(懂1,2):10,20
进阶阶段(懂1,,2,3):20,20
基础知识熟练(懂1,2,3,4):0,20【正确答案】
推荐阅读:JavaSE基础回顾的【5、继承】
上面代码的正真样子:
package com.hhxy.oop;
class A {
int x;//x=0
public A() {
super();
x=10;
run();
}
public void run() {
System.out.println(x);
}
}
class B extends A {
int x;//x=0
public B() {
super();
x = 20;
run();
}
public void run() {
System.out.println(x);
}
}
public class Test {
public static void main(String[] args) {
new B();
}
}
答案解析:
备注:
- 进行等号
int x= 10
赋值时,编译器会自动将它分解成两个步骤:int x;
和x=0;
int x;
在类被加载时就直接进行了初始化,而赋值操作是会被编译器放在构造方法中进行的,构造方法就是用来进行赋值操作的
- 为什么构造方法第一行会什么一定会存在
super();
方法,原因可以参考:JavaSE基础回顾的【5、继承=>继承后子类构造器的特点】思维拓展:
- 如果将Test类中
new B();
换成new A();
、new A().run
、new B().run()
又将会是一个怎样的结果呢?总结:
- 一个类在使用new进行实例化,就会自动调用构造方法
- 变量的初始化发生在类加载阶段,变量的赋值发生在构造方法的执行阶段
- 一个类的构造方法存在默认super();方法
- 方法由谁触发就由谁去执行
4、final关键字学习
finally表示”最终地“,可以修饰变量、方法、类
4.1 用法一:修饰变量
final
修饰的变量被称为常量,只能被赋值一次
- 修饰局部变量:局部变量可以先定义,后赋值(但是定义的时候不会进行初始化)
- 修饰的引用类型的变量:变量存储的地址的值不会改变,但地址指向的对象内容是可以变的
拓展:
Java中赋值的特点:Java是不能直接在类中对变量进行赋值的,只能在定义的时候赋值,或者在方法中赋值,或者在代码块中赋值
final修饰的方法不能被重写
4.3 用法三:修饰类
final修饰的类不能被继承
作用:final修饰的变量一般用来做信息的标识
拓展:常量
经验:常量通常使用
public static final
修饰常量的作用和好处:可做系统的配置信息、方便程序的维护、提高程序的可读性
常量执行原理:在编译阶段,会直接讲常量进行宏替换,替换成常量所对应的真实的字面量
5、enmu关键字学习
- 枚举类所在包:java.lang.Enmu
- 枚举类应用的是多例模式,提前准备了多个对象
- 枚举类是最终类,符合final修饰类的全部特点
- 枚举类的构造器私有,不能实例化对象
语法:
[修饰符] enum 类名 {
object1,object2...;
}
使用Javap命令可以反编译枚举类javac生成的class文件,查看枚举类的真正构造
enum Season{ SPRING , SUMMER , AUTUMN , WINTER; }
javap反编译后
Compiled from "Season.java" public final class Season extends java.lang.Enum<Season> { public static final Season SPRING = new Season();// public static final Season SUMMER = new Season(); public static final Season AUTUMN = new Season(); public static final Season WINTER = new Season(); public static Season[] values(); public static Season valueOf(java.lang.String); }
枚举类的作用是为了做信息的标志和信息的分类
相对于常量做信息的标识,会显得更加严谨,代码可读性更强,对于不符合规范的数据类型还会报错,枚举型是最好的信息分类技术(例如:设计一一个控制方位的按钮就可以使用枚举类)
推荐阅读:Java中enum详解
6、abstract关键字学习
abstract在英文中是”抽象的“意思,正如其名,他修饰的类、方法都是抽象的,空有其表。
注意事项:
abstract
关键字和final
关键字是互斥关系,不能共存。(原因很简单,就不赘述了)
6.1 用法二:修饰方法
被abstract修饰的方法叫做抽象方法
- 抽象方法只有方法名没有方法体
- 抽象方法可以被子类重写
6.2 用法一:修饰类
被abstact修饰的类叫做抽象类
- 抽象类不可以被实例化。原因:抽象类中的方法是抽象方法,抽象方法没有方法体,无法被调用(其实也可以结合实际理解,既然一个东西存一个实体,我们能够摸到、看到它,肯定就不抽象了)
- 抽象类可以被继承,继承的子类必须重写父类中所有的抽象方法
注意:一定不能使用abstract修饰变量、构造方法、代码块
语法:
[修饰符] abstract class 类名{
[[修饰符] abstract 方法返回值类型 方法名([参数]);]
}
抽象类和抽象方法的关系:抽象方法一定在抽象类中,抽象类中不一定有抽象方法,抽象类中的方法不一定是抽象方法
抽象类的作用:提高代码的复用性、可扩展性(提供一个模板)、灵活性
应用场景:
当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
6.3 模板方法设计模式
解决的问题:当一个系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候
模板方法实现步骤:
- Step1:定义一个抽象类
- Step2:定义两种方法,模板方法 和 抽象方法,模板方法中放通用且 方法体确定 的方法,抽象方法用来写功能确定但方法体不确定的方法
- Step3:子类继承抽象类,重写抽象方法
备注:模板方法一定要使用final关键字修饰
原因:模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法(那模板就失去了存在的意义),则模板方法就失效了,因此,加上final后可以防止子类重写了模板方法,这样更安全、专业。
eg:当学校要求中学生和小学生都必须写一篇作文,题目是《我的爸爸》,作文的第一段和最后一段内容要一样,中间介绍各自爸爸的细节不一样。现在使用模板方法来设计:
package com.hhxy.finall;
abstract class Student{
//1.模板方法:写共同部分
public final void write(){
//作文题目《我的爸爸》
System.out.println("《我的爸爸》");
//第一段作文内容:
System.out.println("我的爸爸爱我");
//中间部分:
writeMain();//谁触发就调用谁的方法,和那个面试题中run()方法一样
//最后一段内容:
System.out.println("我爱我的爸爸");
}
//2.抽象方法:写功能确定但内容不确定的部分
public abstract void writeMain();
}
class StudentMiddle extends Student{
@Override
public void writeMain() {
System.out.println("我爸是李刚");
}
}
class StudentSmall extends Student{
@Override
public void writeMain() {
System.out.println("我爸是王键林");
}
}
public class Test {
public static void main(String[] args) {
StudentMiddle sm = new StudentMiddle();
StudentSmall ss = new StudentSmall();
sm.write();
ss.write();
}
}
输出如图所示:
7、interface关键字学习
7.1 接口的特点和用法
interface在英文中有”接口,交流界面“的意思,它用于定义接口。接口本质上是一种特殊的抽象类,它将抽象进行的跟彻底。
JDK8以前:接口中只存在抽象方法和变量 (准确来说是常量,因为接口中的会默认给里面的变量添加
public static final
)JDK8以后:增加了 默认方法 和 静态方法,且它们都必须有方法体
原因:为了提高接口的扩展性。如果一个接口被成功应用了,后期想要对接口增加一些新的需求,就只能增加抽象方法,而抽象方法有必须要所有的实现类去实现,就会显得很麻烦,而默认方法和静态方法的出现就能很好地解决这个问题了。
- JDK9后增加了私有方法:只能在本接口中被其他默认方法或私有方法调用
接口的特点:
- 接口不能实例化对象
- 接口中的变量会默认添加
public static final
修饰符 - 接口中的方法会默认添加
public static
修饰符 - 接口是多继承模式,一个接口可以同时继承多个接口(这些接口一定不能存在规范冲突,比如:同名抽象方法的返回值类型不一样)
- 接口中只存在抽象方法、默认方法、静态方法、私有方法。接口中的静态方法只能通过
接口名.方法名()
来调用4,抽象方法和默认方法只能通过接口的实现类的实例化对象调用(默认方法可以被实现类重写5),私有方法只能在本接口中被其他默认方法或私有方法调用。
语法:
[修饰符] interface 接口名[extends 父接口1,父接口2...]{
[public static final] 数据类型 变量名 = 变量值;//常量
[public] [abstract] 方法名([参数]);//抽象方法
[public] default 方法名([参数]){}//默认方法
[public] static 方法名([参数]){}//静态方法
private 方法名([参数])//私有方法
}
作用:规范代码,约束实现它的类需要完成哪些功能
7.2 implements关键字学习
implements是实现接口的关键字,用于表示接口的实现类。
实现类的特点:
- 抽象类实现接口,只需要实现部分抽象方法(或者一个都不实现)
- 普通类实现接口,需要实现接口中所有的抽象方法
- 一个类允许同时实现多个接口
- 继承和实现可以共存,先继承后实现(优先级:继承>实现,当一个类继承的和实现的方法同名,继承的优先)(有点像亲爸和干爹的关系,亲爸只能有一个,干爹可以有无数个i,亲爸比干爹重要)
语法:
[修饰符] class 类名 [extends 父类] implements 接口1 [,接口2,接口3...]{}
7.3 接口和多态的综合使用案例
本来不打算将这段代码记录进笔记的,太简单了🐶,因为我觉得代码只需要理解看懂就OK了,但个人感觉这个案例对接口和多态的进一步理解有作用,就搬过来了。题目要求是:一台电脑又一个USB接口,鼠标和键盘要又插入和拔出USB接口的功能,同时它们插入后所实现的功能又各自不同
USB接口的定义:
package com.hhxy.demo;
/**
* USB接口:用于规范
* 必须具有的功能:插入、拔出
*/
public interface USB {
void insert();//插入方法
void putOut();//拔出方法
}
键盘类的定义:
package com.hhxy.demo;
/**
*KeyBoard:键盘类
* 需要受到接口的约束:实现接口
*/
public class KeyBoard implements USB{
//定义键盘的成员变量
private String name;//键盘的名字
//提供无参、有参构造方法
public KeyBoard() {
}
public KeyBoard(String name) {
this.name = name;
}
//提供成员变量的get、set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//键盘实现接口中所有的抽象方法
@Override
public void insert() {
System.out.println(name+"成功链接电脑");
}
@Override
public void putOut() {
System.out.println(name+"成功拔出电脑");
}
//键盘的独有功能:点击功能
public void keyDown(){
System.out.println(name+"键盘点击了Enter键!");
}
}
鼠标类的定义:
package com.hhxy.demo;
/**
* Mouse:鼠标
* 需要受到接口的约束:实现接口
*/
public class Mouse implements USB {
//定义鼠标的成员变量
private String name;//鼠标的名字
//提供无参、有参构造方法
public Mouse() {
}
public Mouse(String name) {
this.name = name;
}
//提供成员变量的get、set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//鼠标实现接口中所有的抽象方法
@Override
public void insert() {
System.out.println(name + "成功链接电脑");
}
@Override
public void putOut() {
System.out.println(name + "成功拔出电脑");
}
//鼠标的独有功能:点击功能
public void click() {
System.out.println(name + "双击点亮小红心!");
}
}
电脑类的定义:
package com.hhxy.demo;
public class Computer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Computer(String name) {
this.name = name;
}
//电脑开机
public void run(){
System.out.println("电脑开机了");
}
//鼠标和电脑进行拼接
public void joint(USB obj){
//鼠标或者键盘想链接电脑先插入USB
obj.insert();
//插入后各自进行各自的功能
if(obj instanceof Mouse){
Mouse m = (Mouse) obj;
m.click();
}else{
KeyBoard k = (KeyBoard) obj;
k.keyDown();
}
//鼠标或者电脑链接电脑使用后拔出USB
obj.putOut();
}
}
测试类的定义:
package com.hhxy.demo;
//用于理解接口和多态
public class Test {
public static void main(String[] args) {
//买电脑
Computer c = new Computer("拯救者");
c.run();
//买鼠标、键盘
USB u1 = new KeyBoard("樱桃者");
USB u2 = new Mouse("灵蛇");
//将鼠标和键盘进行拼接
c.joint(u1);
c.joint(u2);
}
}
结语
加油努力,我最是最棒的😃 。下一个目标干JavaWeb
最后附上本文思维导图:
成员变量是类的属性,是直接定义在类中的变量 ↩︎
局部变量成员方法中的变量,定义在方法中 ↩︎
成员就是类的组成部分,可以是类的方法、类的变量(也可以称作类的属性)、代码块 ↩︎
目的:一个类同时实现多个接口时,如果多个接口存在同名的静态方法,不会发生冲突。也可以通过实现类重写静态方法来解决冲突 ↩︎
目的:解决一个类同时实现多个接口时,多个类都存在同一个默认方法,此时会报错,实现类永远无法访问到接口中的默认方法,只能通过实现类重写解决冲突 ↩︎