面向对象三大修饰符
【注1】:回答修饰符关键字的用法时:
- 修饰哪些元素?2.分别有什么含义?
【注2】:和访问修饰符 (private 缺省 protected public) 在使用时,无先后顺序
abstract 抽象
1)修饰类(抽象类)
- 被abstract修饰的类为抽象类
- 抽象类不允许创建对象!绝育了!但可以声明引用!所以可以强制用多态!
public class TestMyClass {
public static void main(String[] args) {
//声明引用
MyClass m = null;
MyClass s = new MySub(); //多态的使用
s.eat();
System.out.println(s.m);
}
}
abstract class MyClass{
int m = 5; //抽象类中可以有属性
public void eat(){ //抽象类中可以有成员方法
System.out.println("吃");
}
}
class MySub extends MyClass{
}
- 语法: 访问修饰符 abstract class 类名{ }
- 好处:强制使用多态
- 抽象类的使用场景:
通常将一些抽象出的父类定义为抽象类:具有某一类事物的特性和行为,(行为定义成抽象方法一定需要重写),但不具有具体的执行功能的类。
【注意事项】
- 在抽象类中,可以定义属性和成员方法。
- 在抽象类中,可以定义构造方法,但是构造方法不是给自己的对象使用的,而是子类对象创建对象时使用。
- 抽象类中也既可以有抽象方法,也可以没有抽象方法。
- 如果一个类,继承抽象类,则该类必须覆盖父类中全部抽象方法,除非该子类还是抽象类。
2)修饰方法(抽象方法)
- 被abstract修饰的方法为抽象方法,只有声明,没有方法的实现。
- 语法:访问修饰符 abstract 返回值类型 方法名(实参列表);
- 【注意事项】
- 抽象方法必须定义在抽象类中。
- 抽象类中不一定要有抽象方法,抽象类中,既可以有抽象方法,可以只有普通方法。
static 静态
可以修饰 成员变量、成员方法、初始代码块
成员变量可以分为两类:
- 静态成员变量——有static修饰,属于类,内存中仅加载一次
- 普通成员变量——无static修饰,属于对象
成员方法可以分成两类:
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
- 实例成员方法(无static修饰,归属于对象),只能用对象触发访问。
修饰成员变量
static修饰的成员变量为静态成员变量,属于类,被该类的所有对象共享。
语法:访问修饰符 static 数据类型 变量名;
访问修饰符 static 数据类型 变量名 = 值;
使用:类名.属性名 //推荐使用
对象名.属性名
修饰方法
被static修饰的方法称为静态方法,属于类,被该类的所有对象共享。
语法:访问修饰符 static 返回值类型 方法名(形参列表){ 方法体 }
使用:类名.方法名(实参)
对象名.方法名(实参)
注意:
- 静态方法中可以直接使用静态属性和静态方法;
- 静态方法中不允许直接使用非静态属性和非静态方法;
- 非静态方法中 可以使用 静态属性和静态方法;
- 静态方法中 不允许使用this和super关键字;
- 静态方法可以被继承,但没有多态(子类中定义和父类相同的方法,当通过父类的引用 调用时,执行的是 父类中的静态方法)
静态成员方法的适用场景
- 表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
- 如果该方法是以执行一个共用功能为目的,则可以声明成静态方法。
static应用知识——工具类
1、工具类是什么?
类中都是一些静态方法,每个方法都是以完成一个公用的功能为目的,这个类是用来给系统开发人员共同使用的。
2、使用工具类的好处
- 调用方便
- 提高了代码的复用性,一次编写处处可用。
/**
* 工具类
*/
public class Util {
/**
* 静态方法
*/
public static String creatVerifyCode(int n){
//1.定义一个变量定义验证码
String code = "";
//2.定义一个变量记住全部验证码字符
String data = "zbcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ";
//3.定义一个循环生成几个随机索引,去得到几个字符
Random r = new Random();
for (int i = 0; i < 5; i++) {
//获取随即索引对应的字符,链接给code
int index = r.nextInt(data.length());
code += data.charAt(index);
}
return code;
}
/**
* 由于工具类无需创建对象,所以将构造器私有化会显得很专业。
*/
private Util(){
}
}
public class Check {
//审核
public static void main(String[] args) {
System.out.println(Util.creatVerifyCode(5));
}
}
3、为什么工具类中的方法不用实例方法做?
- 实例方法需要创建对象调用。
- 此时用对象只是为了调用方法,这样会浪费内存
4、工具类定义时的其他要求:
- 由于工具类里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象。建议将工具类的构造器私有化。
代码块
代码块概述
- 代码块是类的五大成分之一(成员变量、构造器、方法、代码块、内部类)
- 在Java类中,使用{}括起来的代码成为代码块。
静态代码块
- 被static修饰的初始代码块。
- 格式static{ }
- 特点:需要通过static关键字修饰,随着类加载而加载,并且自动触发,只执行一次。
- 适用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
public class StaticDemo1 {
public static String schoolName;
/**
* 静态代码块
* 作用:可以用于初始化静态资源
*/
static{
System.out.println("静态代码块被触发啦");
schoolName = "小禾苗幼儿园";
}
public static void main(String[] args) {
//目标:理解静态代码块
System.out.println("main方法执行");
System.out.println(schoolName);
}
}
-------------------------------------------------------------------
静态代码块被触发啦
main方法执行
小禾苗幼儿园
构造代码块
- 定义在类以内,方法以外的代码块(用{ }括起来的一段代码)
- 格式{}
- 特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行之前执行。
- 适用场景:初始化实例资源。
public class StaticDemo2 {
public StaticDemo2(){
System.out.println("无参构造器被执行");
}
/**
* 实例代码块
*/
{
System.out.println("实例代码块被调用");
}
public static void main(String[] args) {
//目标:理解实例代码块
StaticDemo2 s1 = new StaticDemo2();
}
}
--------------------------------------------------------------------
实例代码块被调用
无参构造器被执行
静态代码块案例
public class StaticTest3 {
/**
* 1.定义一个静态集合,这样这个集合只加载一次,因为当前只需要一副牌
*/
public static ArrayList<String> cards = new ArrayList<>();
/**
* 2.在程序真正运行main方法前。把54张牌放进去,后续游戏直接使用
*/
static{
//3.正式做牌,放到集合中去即可
//a.定义一个数组存储全部点数,类型确定、个数确定,用数组比较好
String[] sizes = {"3", "4", "5", "6", "7", "8", "9","J","Q","K","A"};
//b.定义一个数组存储花色
String[] colors = {"♥" , "♠", "♦", "♣"};
//c.遍历点数
for (int i = 0; i < sizes.length; i++) {
//d.遍历花色
for (int i1 = 0; i1 < colors.length; i1++) {
String card = sizes[i] + colors[i1];
cards.add(card);
}
}
//e.单独加入大小王
cards.add("小🃏");
cards.add("大🃏");
}
public static void main(String[] args) {
//目标:模拟游戏启动前初始化54张牌
System.out.println("新牌" + cards);
}
}
---------------------------------------------------------------------
新牌[3♥, 3♠, 3♦, 3♣, 4♥, 4♠, 4♦, 4♣, 5♥, 5♠, 5♦, 5♣, 6♥, 6♠, 6♦, 6♣, 7♥, 7♠, 7♦, 7♣, 8♥, 8♠, 8♦, 8♣, 9♥, 9♠, 9♦, 9♣, J♥, J♠, J♦, J♣, Q♥, Q♠, Q♦, Q♣, K♥, K♠, K♦, K♣, A♥, A♠, A♦, A♣, 小🃏, 大🃏]
类加载
类加载:【面试重点】
当JVM第一次使用某个类时进行类加载**,通过classpath环境变量找到对应的.class文件,从.class文件中读取该类的相关信息(包名/类名/父类/构造方法/属性/成员方法等等)并保存到JVM内存中,这个过程称类加载(类加载只进行1次)。(将静态常量池内数据存放入方法区的运行时常量池)
类加载的时机:【面试重点】
1> 第1次创建一个类的对象,先类加载,后创建对象
2> 第1次使用一个类的静态属性或静态方法时,会先类加载
3> 子类类加载时,会先进行父类的类加载
- 第1次创建子类的对象时
- 第1次使用子类的静态属性或静态方法时
对象创建的过程
**1> 类加载:**给静态属性分配空间,赋初值,执行静态代码块
- 先父类 类加载(如果有)
父类静态属性->父类的静态代码块
- 后子类 类加载
子类静态属性->子类的静态代码块
2> 创建对象
- 分配内存空间(根据成员变量分配),属性赋默认值
- 递归的创建父类对象:给父类属性赋初值->调父类构造方法
- 给本类属性赋初值
- 调本类构造方法
【小知识】
Scanner sc = new Scanner(System.in);
//System.in 此处in为System类的静态属性
final( 最终的、最后的)
1、final可以修饰变量(局部变量、实例变量、静态变量)
(1)final修饰的局部变量,只允许一次赋值,一旦赋值不允许修改,可以多次使用。
public static void main(String[] args) {
final int a = 7; //局部变量
//a = 9; java: 无法为最终变量a分配值
System.out.println("a + 9" + "=" + (a + 9)); //a + 9=16
}
(2)final修饰的实例变量,jvm不再分配默认值,初始化时机:
a.在声明的同时完成初始化。
b.利用构造方法完成初始化
【报错:java: 变量 m 未在默认构造器中初始化】
final修饰的变量,只能赋值一次,如果Java给赋值默认值,开发者无法赋值
public class TestFinal {
public static void main(String[] args) {
MyClas mc = new MyClas();
System.out.println(mc.m); //报错!!!!!
}
}
class MyClas{
final int m;
}
可以在构造方法中为final修饰的成员变量赋值
public class TestFinal {
public static void main(String[] args) {
MyClas mc = new MyClas(9);
System.out.println(mc.m);
}
}
class MyClas{
final int m; //实例变量
public MyClas(int m) {
this.m = m;
}
}
(3)final修饰的静态变量,不再分配默认值,初始化时机:
a.在声明的同时,完成初始化
b.在静态代码块中,完成初始化
final static int m = 9;
class MyClas{
final static int n;
//静态代码块:类加载时执行。
static{
n = 10;
}
}
(4)被final修饰的引用,代表引用中存储的地址不可以改变,但对象本人可以改变。
public class TestFinal {
public static void main(String[] args) {
final MyClas mc; //局部变量
mc = new MyClas(); //引用存储的是对象的首地址
mc.m = 10;
System.out.println(mc.m); //10
// mc.n = 100 报错 不允许被修改
}
}
class MyClas{
int m = 3; //实例变量,数据可以被改变
final int n = 8; //实例变量,数据不允许改变
}
2、final修饰方法:被final修饰的方法能被子类继承,但是不允许覆盖
class MyClas{
int m = 3; //实例变量,数据可以被改变
final int n = 8; //实例变量,数据不允许改变
public final void m1(){
System.out.println("m1时final修饰的,可以继承到,不能被覆盖");
}
}
class Sub extends MyClas{
// public void m1(){ 报错
// java: Sub中的m1()无法覆盖MyClas中的m1()
// }
}