面向对象-day04
今日学习内容:
- this关键字
- super关键字
- static修饰符
- final修饰符
- 代码块
- 内部类
- 枚举
今日学习目标:
- 重点掌握this关键字的含义和用法
- 重点掌握super关键字的含义和用法
- 掌握final修饰符的修饰类,方法,变量的含义
- 了解代码块有那些
- 掌握静态代码块的语法
- 了解内部类有哪些
- 掌握匿名内部类的语法
- 掌握枚举类的定义和使用
13. 面向对象查漏补缺
13.1. this关键字(重点掌握)
什么是this:表示当前对象
this主要存在于两个位置:
- 在构造器中:表示当前被创建的对象
- 在 方法中:哪一个对象调用this所在的方法,此时this就表示哪一个对象
public class Cat {
private String name;
private int age;
public Cat() {
System.out.println("构造器中:" + this); //当前创建的对象
}
public void say() {
System.out.println("say方法中:" + this); //当前调用say方法的对象
System.out.println("名字=" + this.name + ",年龄=" + this.age);
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
测试代码:
public class ThisDemo {
public static void main(String[] args) {
Cat c1 = new Cat(); //此时构造中的this就是当前创建的c1对象
c1.setName("加菲");
c1.setAge(5);
c1.say(); //此时say方法中的this就是c1对象
Cat c2 = new Cat();//此时构造中的this就是当前创建的c2对象
c2.setName("Tom");
c2.setAge(3);
c2.say(); //此时say方法中的this就是c2对象
}
}
运行测试:
构造器中 :cn.wolfcode._01_this.Cat@15db9742
say方法中 :cn.wolfcode._01_this.Cat@15db9742
名字=加菲,年龄=5
构造器中 :cn.wolfcode._01_this.Cat@6d06d69c
say方法中 :cn.wolfcode._01_this.Cat@6d06d69c
名字=Tom,年龄=3
什么时候需要使用this:
- 解决局部变量和成员变量之间的二义性,此时必须使用
- 同一个类中非static方法间互调(此时可以省略this,但是不建议省略)
- 构造器重载的互调(看懂即可)
public class Dog {
private String name;
private int age;
public Dog() {
}
public Dog(String name) {
this(name, 0);// 调用两个参数的构造器,必须放在构造器第一行
// TODO其他操作
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public void say() {
String name = "局部变量";
System.out.println(name); // 访问局部变量
System.out.println(this.name); // 访问成员变量
this.other();// 调用当前类中非static方法
}
public void other() {
System.out.println(this.age);//此时的this是谁
}
}
13.2. super关键字(重点掌握)
什么是super:
this :表示当前对象,谁调用this所在的方法,this就是哪一个对象
super :当前对象的父类对象
在创建子类对象时,在子类构造器的第一行会先调用父类的构造器。
什么时候使用super:
- 在子类方法中,调用父类被覆盖的方法,此时必须使用super
- 在子类构造器中,调用父类构造器,此时必须使用super语句
父类代码:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void doWork() {
System.out.println("Person...doWork...");
}
}
子类代码:
public class Student extends Person {
private String sn;
public Student(String sn) {
super();// 隐式调用父类无参数构造器,必须作为构造第一行
this.sn = sn;
}
public Student(String name, int age, String sn) {
super(name, age);// 显示去调用父类无参数构造器,必须作为构造第一行
this.sn = sn;
}
public void doWork() {
super.doWork(); // ?此时调用谁的方法
this.doWork(); // ?此时调用谁的方法
System.out.println("Student...doWork...");
}
}
13.3. static修饰符(掌握)
static修饰的字段和方法直接属于类,不属于该类的对象。记住:字段和方法属于谁,就让谁来调用。
- 使用static修饰的成员: 属于类 直接使用类名调用即可
- 没有使用static修饰的成员: 属于对象 必须先创建对象,再调用
注意:static方法不能使用super和this:
因为static是类级别的,super和this是对象级别的,存在类的时候不一定存在对象,也就说使用类名调用static方法时,此时可能是没有对象的。
测试代码:
public class Dog {
public static int totalNumber = 100;
public int age;
public void m1() {
System.out.println("实例方法");
}
public static void m2() {
System.out.println("静态方法");
}
}
public class StaticDemo {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.age = 5;
Dog d2 = new Dog();
d2.age = 15;
//调用代码再后面
}
}
内存分析:
static修饰的成员变量(字段),随着所在类被加载进JVM,也同时存储在方法区中,被所有对象共享。
实例成员和类成员的访问规则:
d1.m1();// 实例方法
d2.m1();// 实例方法
// Dog.m1(); 语法报错
d1.m2();//静态方法 底层使用类名访问
d2.m2();//静态方法 底层使用类名访问
d1.m2();//静态方法 底层使用类名访问
Dog.m2();
System.out.println(d1.age);// 5
System.out.println(d2.age);// 15
// System.out.println(Dog.age); 语法报错
System.out.println(d1.totalNumber);//100 底层使用类名访问
System.out.println(d2.totalNumber);//100 底层使用类名访问
System.out.println(Dog.totalNumber);//100
使用对象访问static方法或成员变量,底层依然使用类名访问的。
一般的,static方法访问的成员变量必须使用static修饰。
最后记住结论:
- 类 成 员:使用static修饰的字段和方法 : 属于类 直接使用类名调用即可
- 实例成员:没有使用static修饰的字段和方法: 属于对象 必须先创建对象,再调用
13.4. final修饰符(掌握)
继承关系最大弊端是破坏封装,子类可以继承父类的实现细节,也可以通过方法覆盖的形式修改功能实现细节。那么怎么来限制某个类不能有子类,不能覆盖方法?——final修饰符。
final的含义是最终的,不可改变的,可以修饰类、方法、变量。
- final修饰的类:表示最终的类, 该类不能再有子类
final public class Super {
}
public class Sub extends Super{ //此行语法报错
}
- final修饰的方法:最终的方法,该方法不能被子类覆盖
public class Super {
final public void doWork() {
}
}
public class Sub extends Super{
public void doWork() { //此行语法报错
}
}
- final修饰的变量:表示常量,该变量只能赋值一次,不能再重新赋值。
- 基本数据类型:表示的值不能改变
- 引用数据类型:所引用的地址值不能改变
final int age = 17;
age = 100; //此行语法报错
final Dog d = new Dog();
d.setAge(5); //d的字段值是可以改变的
d = new Dog(); //此行语法报错
13.5. 代码块(了解)
什么是代码块:直接使用{}括起来的一段代码区域。
代码块里变量属于局部变量,只在自己所在区域{}内有效。
存在三种形式:
- 局部代码块: 直接定义在方法内部的代码块,一般的,不会直接使用局部代码块的,结合if、while、for等关键字使用,表示一块代码区域。
public class CodeBlockDemo {
public static void main(String[] args) {
System.out.println("begin...");
{
//直接使用代码块,一般不用
int age = 17;
}
System.out.println(age); //此行报错,超出age作用范围,就不能访问到了
if (100 > 5) {
System.out.println("100 > 5");
}
System.out.println("end...");
}
}
- 初始化代码块(构造代码块):直接定义在类中。每次创建对象的时候都会执行初始化代码块,开发中不使用初始化代码块,即使要做初始化操作,可以直接在构造器中完成即可。
- 静态代码块:使用static修饰的初始化代码块,当该代码块的类的字节码被加载进JVM,就执行static代码块代码。在开发中,用来做加载资源、加载配置文件等操作。
分析下面代码执行顺序:
public class Fish {
{
System.out.println("初始化代码块...");
}
static {
System.out.println("静态代码块...");
}
public Fish() {
System.out.println("构造器");
}
public static void main(String[] args) {
System.out.println("主方法...");
new Fish();
new Fish();
}
}
运行结果:
静态代码块...
主方法...
初始化代码块...
构造器
初始化代码块...
构造器
13.6. 内部类和匿名内部类(了解)
什么是内部类,把一个类定义在另一个类的内部,把里面的类称之为内部类,把外面的类称之为外部类。(能认识内部类即可)
内部类可以看作和字段、方法一样,是外部类的成员,而成员可以有static修饰。
- 静态内部类:使用static修饰的内部类,那么访问内部类直接使用外部类名来访问
- 实例内部类:没有使用static修饰的内部类,访问内部类使用外部类的对象来访问
- 局部内部类:定义在方法中的内部类,一般不用
- 匿名内部类:特殊的局部内部类,适合于仅使用一次使用的类
对于每个内部类来说,Java编译器会生成独立.class文件。
- 静态和实例内部类:外部类名$内部类名字-
- 局部内部类:外部类名$数字内部类名称
- 匿名内部类:外部类名$数字
13.6.1. 匿名内部类(重点掌握)
在多态USB的案例中,当新增一种USB规范的设备,此时需要单独使用一个文件来定义一个新的类。
比如,新增一个USB规范的打印机设备。
public class Print implements IUSB{
public void swapData() {
System.out.println("打印....");
}
}
把打印机安装在主板上。
public class USBDemo {
public static void main(String[] args) {
// 创建主板对象
MotherBoard board = new MotherBoard();
// 创建打印机对象
Print p = new Print();
//把打印机安装在主板上
board.plugin(p);
}
}
如果这一个Print类只需要使用一次的话,就完全没有必要单独定义一个Java文件,直接使用匿名内部类来完成。
匿名内部类,可以使用父类构造器和接口名来完成。
针对类,定义匿名内部类来继承父类(使用较少):
new 父类构造器([实参列表]){
//匿名内部类的类体部分
}
针对接口,定义匿名内部类来实现接口(使用较多):
new 接口名称(){
//匿名内部类的类体部分
}
注意:这里不是根据接口创建对象,而是一种语法而已。
board.plugin(new IUSB() {
public void swapData() {
System.out.println("打印...打印...");
}
});
其实匿名内部类,底层依然还是创建了一份字节码文件USBDemo$1,其反编译代码为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32TiMnvk-1693278427629)(img\图片 3_6.png)]
13.7. 枚举类(掌握)
13.7.1. 枚举的诞生史(了解)
在服装行业,衣服的分类根据性别可以表示为三种情况:男装、女装、中性服装。
private ? type;
public void setType(? type){
this.type = type
}
需求:定义一个变量来表示服装的分类?请问该变量的类型使用什么?
使用int和String类型,且先假设使用int类型,因为分类情况是固定的,为了防止调用者乱创建类型,可以把三种情况使用常量来表示。
public class ClothType {
public static final int MEN = 0;
public static final int WOMEN = 1;
public static final int NEUTRAL = 2;
}
注意:常量使用final修饰,并且使用大小字面组成,如果是多个单词组成,使用下划线分割。
此时调用setType方法传递的值应该是ClothType类中三个常量之一。但是此时依然存在一个问题——依然可以乱传入参数比如100,此时就不合理了。
同理如果使用String类型,还是可以乱设置数据。那么说明使用int或String是类型不安全的。那么如果使用对象来表示三种情况呢?
public class ClothType {
public static final ClothType MEN = new ClothType();
public static final ClothType WOMEN = new ClothType();
public static final ClothType NEUTRAL = new ClothType();
}
此时调用setType确实只能传入ClothType类型的对象,但是依然不安全,为什么?因为调用者可以自行创建一个ClothType对象,如:setType(new ClothType())。
此时为了防止调用者私自创建出新的对象,我们把CLothType的构造器私有化起来,外界就访问不了了,此时调用setType方法只能传入ClothType类中的三个常量。此时代码变成:
public class ClothType {
public static final ClothType MEN = new ClothType();
public static final ClothType WOMEN = new ClothType();
public static final ClothType NEUTRAL = new ClothType();
private ClothType() {}
}
高,实在是高!就是代码复杂了点,如果存在定义这种类型安全的且对象数量固定的类的语法,再简单点就更好了——有枚举类。
13.7.2. 枚举类的定义和使用(掌握)
枚举是一种特殊的类,固定的一个类只能有哪些对象,定义格式:
public enum 枚举类名{
常量对象A, 常量对象B, 常量对象C ;
}
我们自定义的枚举类在底层都是直接继承了java.lang.Enum类的。
public enum ClothType {
MEN, WOMEN, NEUTRAL;
}
枚举中都是全局公共的静态常量,可以直接使用枚举类名调用。
ClothType type = ClothType.MEN;
因为java.lang.Enum类是所有枚举类的父类,所以所有的枚举对象可以调用Enum类中的方法.
String name = 枚举对象.name(); // 返回枚举对象的常量名称
int ordinal = 枚举对象.ordinal(); // 返回枚举对象的序号,从0开始
int ordinal = 枚举对象.ordinal(); // 返回枚举对象的序号,从0开始
注意:枚举类不能使用创建对象
public class EnumDemo {
public static void main(String[] args) {
int ordinal = ClothType.MEN.ordinal();
String name = ClothType.MEN.name();
System.out.println(ordinal);
System.out.println(name);
new ClothType(); //语法报错
}
}
目前,会定义枚举类和基本使用就可以了,后面还会讲更高级的使用方式。
学习优势:
1.包含java前后端从 0 ->1 全过程教学, 内容全面, 知识点不遗漏, 学完即可参加实际工作.
2.课程为目前项目开发常用的技术知识,向用人单位对标,学以致用。那些脱离实际,废弃不用的,太前沿的框架技术前期不建议学。
3.一起学习,打卡,一起交流,希望能营造一个和线下一样的学习环境。