当前位置: 首页 > news >正文

java复习笔记-面向对象

Java-面向对象

  • 一、类和对象
    • 1. 1 对象在JVM中的内存解析
    • 1. 2 成员变量和局部变量
    • 1. 3 方法调用的内存分析
    • 1. 4 方法的重载
  • 二、封装性(encapsulation)
  • 三、继承性
    • 3. 1 子类对象实例化过程
  • 四、多态性
    • 4. 1 Object类的使用
  • 五 、static 关键字
  • 六、单例(Singleton)设计模式 和 代码块
  • 七、抽象类和抽象方法
  • 八、接口
  • 九、内部类
  • 十、枚举类

面向过程的程序设计思想(Process-Oriented Programming),简称 POP关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便于维护。

典型的语言:C 语言
代码结构:以函数为组织单位。
是一种“执行者思维”,适合解决简单问题。扩展能力差、后期维护难度较大。


面向对象的程序设计思想( Object Oriented Programming),简称 OOP关注的焦点是类:在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,用类来表示。

典型的语言:Java、C#、C++、Python、Ruby 和 PHP 等
代码结构:以类为组织单位。每种事物都具备自己的属性和行为/功能。
是一种“设计者思维”,适合解决复杂问题。代码扩展性强、可维护性高。

面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。

千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!

一、类和对象

类(Class)和对象(Object)是面向对象的核心概念。

类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。

对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例

在这里插入图片描述

可以理解为:类 => 抽象概念的人;对象 => 实实在在的某个人

属性:该类事物的状态信息。对应类中的成员变量

行为:该类事物要做什么操作,或者基于事物的状态能做什么。对应类中的成员方法


匿名对象: 我们也可以不定义对象的句柄,而直接调用这个对象的方法。例如:new Person().shout();

使用情况
– 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
– 我们经常将匿名对象作为实参传递给一个方法调用。


1. 1 对象在JVM中的内存解析

在这里插入图片描述

堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在 Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配(存储引用对象)。

栈(Stack):是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。(主要存储Java定义的方法状态)

方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

示例:

class Person { //类:人String name;int age;boolean isMale;
}public class PersonTest { //测试类public static void main(String[] args) {Person p1 = new Person();p1.name = "赵同学";p1.age = 20;p1.isMale = true;Person p2 = new Person();p2.age = 10;Person p3 = p1;p3.name = "郭同学";}
}

在这里插入图片描述

:凡是 new 出来的结构(对象、数组)都放在堆空间中。对象的属性存放在堆空间中。创建一个类的多个对象(比如 p1、p2),则每个对象都拥有当前类的一套"副本"(即属性)。当通过一个对象修改其属性时,不会影响其它对象此属性的值。当声明一个新的变量使用现有的对象进行赋值时(比如 p3 = p1),此时并没有在堆空间中创建新的对象。而是两个变量共同指向了堆空间中同一个对象。当通过一个对象修改属性时,会影响另外一个对象对此属性的调用。

类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。

1. 2 成员变量和局部变量

成员变量:在方法体外,类体内声明的变量称为成员变量。

局部变量:在方法体内部位置声明的变量称为局部变量。

在这里插入图片描述

在这里插入图片描述

static 可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。

成员变量 与 局部变量 的对比

相同点:

  • 变量声明的格式相同: 数据类型 变量名 = 初始化值
  • 变量必须先声明、后初始化、再使用。
  • 变量都有其对应的作用域。只在其作用域内是有效的

不同点:

1、声明位置和方式
(1)实例变量:在类中方法外
(2)局部变量:在方法体{}中或方法的形参列表、代码块中
2、在内存中存储的位置不同
(1)实例变量:堆 (实例变量属于类,为引用类型对象,存储在堆内存中)
(2)局部变量:栈 (局部变量方法体内有效,属于方法状态信息,存储在JVM虚拟机栈中)
3、生命周期(取决于在JVM内存中的存储区域)
(1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被 GC 回收而消亡, 而且每一个对象的实例变量是独立的。
(2)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡, 而且每一次方法调用都是独立。
4、作用域
(1)实例变量:通过对象就可以使用,本类中直接调用,其他类中“对象.实例变量”
(2)局部变量:出了作用域就不能使用
5、修饰符(后面来讲)
(1)实例变量:public,protected,private,final,volatile,transient 等
(2)局部变量:final
6、默认值
(1)实例变量:有默认值
(2)局部变量:没有,必须手动初始化。其中的形参比较特殊,靠实参给它初始化。

1. 3 方法调用的内存分析

方法没有被调用的时候,都在方法区(永久代/元空间) 中的字节码文件(.class)中存储。

方法被调用的时候,需要进入到栈内存中运行。方法每调用一次就会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。

当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。

在这里插入图片描述

1. 4 方法的重载

方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。

参数列表不同,意味着参数个数或参数类型的不同

重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。

重载方法调用:JVM 通过方法的参数列表,调用匹配的方法。

  • 先找个数、类型最匹配的
  • 再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错
判 断与 void show(int a,char b,double c){}构成重载的有:a)void show(int x,char y,double z){} // no
b)int show(int a,double c,char b){} // yes
c) void show(int a,double c,char b){} // yes
d) boolean show(int c,char b){} // yes
e) void show(double c){} // yes
f) double show(int x,char y,double z){} // no
g) void shows(){double c} // no

可变个数的形参:

在 JDK 5.0 中提供了 Varargs(variable number of arguments)机制。即当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。

格式:
方法名(参数的类型名 ...参数名)
举例:
//JDK 5.0 以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books);
  • 可变参数:方法参数部分指定类型的参数个数是可变多个:0 个,1 个或多个可变个数形参的方法与同名的方法之间,彼此构成重载
  • 可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。
  • 方法的参数部分有可变形参,需要放在形参声明的最后在一个方法的形参中,最多只能声明一个可变个数的形参
    public class MathTools {//求两个整数的最大值public int max(int a, int b) {return a > b ? a : b;}//求两个小数的最大值public double max(double a, double b) {return a > b ? a : b;}//求三个整数的最大值public int max(int a, int b, int c) {return max(max(a, b), c);}//求 n 个整数的最大值public int max(int... nums) {int max = nums[0];//如果没有传入整数,或者传入 null,这句代码会报异常for (int i = 1; i < nums.length; i++) {if (nums[i] > max) {max = nums[i];}}return max;}}

形参和实参:
形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。

实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。

参数传递机制:值传递

Java 里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。

形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参

二、封装性(encapsulation)

什么是封装:
所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息。

通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

为什么需要封装性:

随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”。

内聚,指一个模块内各个元素彼此结合的紧密程度;
耦合指一个软件结构内不同模块之间互连程度的度量。
内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

“高内聚,低耦合”的体现之一:

• 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;

• 低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。

如何实现封装:

实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制。

权限修饰符:public、protected、缺省、private。具体访问范围如下:

修饰符类内部包内部其他包子类其他包非子类
private×××
缺省××
protected×
public

私有化类的成员变量,提供公共的 get 和 set 方法,对外暴露获取和修改属性的功能。

① 使用 private 修饰成员变量
private 数据类型 变量名 ;
代码如下:
public class Person {private String name;private int age;private boolean marry;
}
② 提供 getXxx 方法 / setXxx 方法,可以访问成员变量,代码如下:
public class Person {private String name;private int age;private boolean marry;
public void setName(String n) {
name = n;}public String getName() {return name;
}public void setAge(int a) {age = a;}public int getAge() {return age;}public void setMarry(boolean m){marry = m;}public boolean isMarry(){return marry;}
}③ 测试:
public class PersonTest {public static void main(String[] args) {Person p = new Person();//实例变量私有化,跨类是无法直接使用的
/* p.name = "张三";p.age = 23;p.marry = true;*/p.setName("张三");System.out.println("p.name = " + p.getName());p.setAge(23);System.out.println("p.age = " + p.getAge());p.setMarry(true);System.out.println("p.marry = " + p.isMarry());}
}

封装的好处:

让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。

便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。
例如:Java8->Java9,String 从 char[]转为 byte[]内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。

开发中,一般成员实例变量都习惯使用 private 修饰,再提供相应的public 权限的 get/set 方法访问。对于 final 的实例变量,不提供 set()方法。对于 static final 的成员变量,习惯上使用 public 修饰。

三、继承性

继承有延续(下一代延续上一代的基因、财富)、扩展(下一代和上一代又有所不同)的意思。

为了描述和处理个人信息,定义类Person:
在这里插入图片描述
为描述和处理学生信息,定义类 Student:
在这里插入图片描述
通过继承,简化 Student 类的定义:
在这里插入图片描述

Student 类继承了父类 Person 的所有属性和方法,并增加了一个属性 school。Person 中的属性和方法,Student 都可以使用。

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成继承关系。

集成的好处:
继承的出现减少了代码冗余,提高了代码的复用性。

  • 继承的出现,更有利于功能的扩展。
  • 继承的出现让类与类之间产生了 is-a 的关系,为多态的使用提供了前提。
  • 继承描述事物之间的所属关系,这种关系是:is-a 的关系。可见,父类更通用、更一般,子类更具体。

不要仅为了获取其他类中某个功能而去继承!

继承的细节说明:

子类会继承父类所有的实例变量和实例方法:

从类的定义来看,类是一类具有相同特性的事物的抽象描述。父类是所有子类共同特征的抽象描述。而实例变量和实例方法就是事物的特征,那么父类中声明的实例变量和实例方法代表子类事物也有这个特征。

当子类对象被创建时,在堆中给对象申请内存时,就要看子类和父类都声明了什么实例变量,这些实例变量都要分配内存。

当子类对象调用方法时,编译器会先在子类模板中看该类是否有这个方法,如果没找到,会看它的父类甚至父类的父类是否声明了这个方法,遵循从下往上找的顺序,找到了就停止,一直到根父类都没有找到,就会报编译错误。

所以继承意味着子类的对象除了看子类的类模板还要看父类的类模板。

子类不能直接访问父类中私有的(private)的成员变量和方法

子类虽会继承父类私有(private)的成员变量,但子类不能对继承的私有成员变量直接进行访问,可通过继承的 get/set 方法进行访问。
在这里插入图片描述

在 Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”

子类在继承父类以后,还可以定义自己特有的方法,这就可以看做是对父类功能上的扩展。

Java 支持多层继承(继承体系)
在这里插入图片描述

子类和父类是一种相对的概念,顶层父类是 Object 类。所有的类默认继承 Object,作为父类。

Java 只支持单继承,不支持多重继承

在这里插入图片描述

子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。
子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型。(例如:Student < Person)。
注意:如果返回值类型是基本数据类型和 void,那么必须是相同

子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限。(public >protected > 缺省 > private)
注意:① 父类私有方法不能重写 ② 跨包的父类缺省的方法也不能重写
子类方法抛出的异常不能大于父类被重写方法的异常

此外,子类与父类中同名同参数的方法必须同时声明为非 static 的(即为重写),或者同时声明为 static 的(不是重写)。因为 static 方法是属于类的,子类无法覆盖父类的方法。

方法的重写:在同一个类中,可以定义多个同名的方法,但它们的参数列表必须不同。返回类型、访问修饰符等可以相同也可以不同。

方法的重载:发生在子类与父类之间,子类对父类中被声明为非私有(public、protected或默认访问级别)的方法进行重新定义,要求方法名、参数列表完全相同,返回类型可以是父类方法返回类型的子类,访问修饰符不能更严格,抛出的异常不能比父类方法更多更广。

3. 1 子类对象实例化过程

在这里插入图片描述

class Creature {public Creature() {System.out.println("Creature 无参数的构造器");
}
}class Animal extends Creature {public Animal(String name) {System.out.println("Animal 带一个参数的构造器,该动物的 name 为"+ name);}public Animal(String name, int age) {this(name);System.out.println("Animal 带两个参数的构造器,其 age 为" + age);}
}public class Dog extends Animal {public Dog() {super("汪汪队阿奇", 3);System.out.println("Dog 无参数的构造器");}public static void main(String[] args) {new Dog();}
}

在这里插入图片描述
在这里插入图片描述

四、多态性

多态性,是面向对象中最重要的概念,在 Java 中的体现:对象的多态性:父类的引用指向子类的对象

格式:(父类类型:指子类继承的父类类型,或者实现的接口类型)
父类类型 变量名 = 子类对象;

Person p = new Student();
Object o = new Person();//Object 类型的变量 o,指向 Person 类型的对象
o = new Student(); //Object 类型的变量 o,指向 Student 类型的对象

对象的多态:在 Java 中,子类的对象可以替代父类的对象使用。所以,一个引用类型变量可能指向(引用)多种不同类型的对象

多态的理解

Java 引用变量有两个类型:编译时类型运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。

  • 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
  • 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法) “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
public class TestPet {public static void main(String[] args) {//多态引用Pet pet = new Dog();pet.setNickname("小白");//多态的表现形式/*编译时看父类:只能调用父类声明的方法,不能调用子类扩展的方法;运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法体;*/pet.eat();//运行时执行子类 Dog 重写的方法// pet.watchHouse();//不能调用 Dog 子类扩展的方法pet = new Cat();pet.setNickname("雪球");pet.eat();//运行时执行子类 Cat 重写的方法}
}

编译时,只能调用编译时类型(即声明对象)的方法和属性。
运行时,实际调用的是引用对象的方法和属性。

因为多态的缘故,想调用子类的扩展方法只能进行类型转换。

为什么需要多态性(polymorphism)?
开发中,有时我们在设计一个数组、或一个成员变量、或一个方法的形参、返回值类型时,无法确定它具体的类型,只能确定它是某个系列的类型。

多态的好处和弊端

好处:变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。

弊端:一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。

Student m = new Student();
m.school = "pku"; //合法,Student 类有 school 成员变量
Person e = new Student();
e.school = "pku"; //非法,Person 类没有 school 成员变量
// 属性是在编译时确定的,编译时 e 为 Person 类型,没有 school 成员变量,因而编译错误。

开发中:
使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。

开闭原则 OCP:对扩展开放,对修改关闭通俗解释:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能

虚方法调用(Virtual Method Invocation)
在 Java 中虚方法是指在编译阶段不能确定方法的调用入口地址,在运行阶段才能确定的方法,即可能被重写的方法。

Person e = new Student();
e.getInfo(); //调用 Student 类的 getInfo()方法

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

在这里插入图片描述
前提:Person 类中定义了 welcome()方法,各个子类重写了 welcome()。


静态链接(或早起绑定):当一个字节码文件被装载进 JVM 内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接。那么调用这样的方法,就称为非虚方法调用。比如调用静态方法、私有方法、final 方法、父类构造器、本类重载构造器等。

动态链接(或晚期绑定):如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此也就被称之为动态链接。调用这样的方法,就称为虚方法调用。比如调用重写的方法(针对父类)、实现的方法(针对接口)。


成员变量没有多态性

若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。

对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量这个实例变量依然不可能覆盖父类中定义的实例变量

向上转型与向下转型

首先,一个对象在 new 的时候创建是哪个类型的对象,它从头至尾都不会变。即这个对象的运行时类型,本质的类型用于不会变。但是,把这个对象赋值给不同类型的变量时,这些变量的编译时类型却不同。

因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。
但是,使用父类变量接收了子类对象之后,我们就不能调用子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换,使得编译通过。

在这里插入图片描述
向上转型:当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型

  • 此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
  • 但是,运行时,仍然是对象本身的类型,所以执行的方法是子类重写的方法体。
  • 此时,一定是安全的,而且也是自动完成的

向下转型:当左边的变量的类型(子类)<右边对象/变量的编译时类型(父类),我们就称为向下转型

  • 此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
  • 但是,运行时,仍然是对象本身的类型
  • 不是所有通过编译的向下转型都是正确的,可能会发生ClassCastException,为了安全,可以通过 isInstanceof 关键字进行判断

向上转型:自动完成
向下转型:(子类类型)父类变量

public class ClassCastTest {public static void main(String[] args) {//没有类型转换Dog dog = new Dog();//dog 的编译时类型和运行时类型都是 Dog//向上转型Pet pet = new Dog();//pet 的编译时类型是 Pet,运行时类型是 Dogpet.setNickname("小白");pet.eat();//可以调用父类 Pet 有声明的方法 eat,但执行的是子类重写的eat 方法体// pet.watchHouse();//不能调用父类没有的方法 watchHouseDog d = (Dog) pet;System.out.println("d.nickname = " + d.getNickname());d.eat();//可以调用 eat 方法d.watchHouse();//可以调用子类扩展的方法 watchHouseCat c = (Cat) pet;//编译通过,因为从语法检查来说,pet 的编译时类型是 Pet,Cat 是 Pet 的子类,所以向下转型语法正确//这句代码运行报错 ClassCastException,因为 pet 变量的运行时类型是Dog,Dog 和 Cat 之间是没有继承关系的}
}

为了避免 ClassCastException 的发生,Java 提供了 instanceof 关键字,给引用变量做类型的校验。

只要用 instanceof 判断返回 true 的,那么强转为该类型就一定是安全的,不会报 ClassCastException 异常。

  • 如果对象 a 属于类 A 的子类 B,a instanceof A 值也为 true。
  • 要求对象 a 所属的类与类 A 必须是子类和父类的关系,否则编译错误。
public class TestInstanceof {public static void main(String[] args) {Pet[] pets = new Pet[2];pets[0] = new Dog();//多态引用pets[0].setNickname("小白");pets[1] = new Cat();//多态引用pets[1].setNickname("雪球");for (int i = 0; i < pets.length; i++) {pets[i].eat();if(pets[i] instanceof Dog){Dog dog = (Dog) pets[i];dog.watchHouse();}else if(pets[i] instanceof Cat){Cat cat = (Cat) pets[i];cat.catchMouse();}}}

4. 1 Object类的使用

根据 JDK 源代码及 Object 类的 API 文档,Object 类当中包含的方法有 11 个。

类 java.lang.Object 是类层次结构的根类,即Object类是所有其它类的父类。每个类都使用 Object 作为超类。

在这里插入图片描述


Object 类型的变量与除 Object 以外的任意引用数据类型的对象都存在多态引用
method(Object obj){} //可以接收任何类作为其参数
Person o = new Person();
method(o);所有对象(包括数组)都实现这个类的方法。
如果一个类没有特别指定父类,那么默认则继承自 Object 类。例如:
public class Person {
...
}
//等价于:
public class Person extends Object {
...
}

equal方法的使用

= =:

  • 基本类型比较值:只要两个变量的值相等,即为 true。
  • 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回 true。

用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错

equals: 所有类都继承了 Object,也就获得了 equals()方法。还可以重写。

  • 只能比较引用类型,Object 类源码中 equals()的作用与“==”相同:比较是否指向同一个对象。

在这里插入图片描述

特例:当用 equals()方法进行比较时,对类 File、String、Date 及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;原因:在这些类中重写了 Object 类的 equals()方法。

重写 equals()方法的原则

  • 对称性:如果 x.equals(y)返回是“true”,那么 y.equals(x)也应该返回是“true”。
  • 自反性:x.equals(x)必须返回是“true”。
  • 传递性:如果 x.equals(y)返回是“true”,而且 y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
  • 一致性:如果 x.equals(y)返回是“true”,只要 x 和 y 内容一直不变,不管你重复 x.equals(y)多少次,返回都是“true”。
  • 任何情况下,x.equals(null),永远返回是“false”;x.equals(和 x 不同类型的对象)永远返回是“false”。

== 和 equals 的区别

  • == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
  • equals 的话,它是属于 java.lang.Object 类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到 String 等类的 equals 方法是被重写过的,而且 String 类在日常开发中用的比较多,久而久之,形成了 equals 是比较值的错误观点。
  • 具体要看自定义类里有没有重写 Object 的 equals 方法来判断。通常情况下,重写 equals 方法,会比较类中的相应属性是否都相等

native 关键字的理解
使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非Java 语言实现的,并且被编译成了 DLL,由 Java 去调用

  • 本地方法是有方法体的,用 c 语言编写。由于本地方法的方法体源码没有对我们开源,所以我们看不到方法体
  • 在 Java 中定义一个 native 方法时,并不提供实现体。

为什么要用 native 方法

Java 使用起来非常方便,然而有些层次的任务用 java 实现起来不容易,或者我们对程序的效率很在意时,例如:Java 需要与一些底层操作系统或某些硬件交换信息时的情况。

native 方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解 Java 应用之外的繁琐的细节。

native 声明的方法,对于调用者,可以当做和其他 Java 方法一样使用

native method 的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM 将控制调用本地方法的所有细节。

JVM内存中的本地方法栈存储的就是Native方法。

五 、static 关键字

如果想让一个成员变量被类的所有实例所共享,就用 static 修饰即可,称为类变量(或类属性)!

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字才会产出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。例如,所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。

在这里插入图片描述

此外,在类中声明的实例方法,在类的外面必须要先创建对象,才能调用。但是有些方法的调用者和当前类的对象无关,这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。这里的类变量、类方法,只需要使用 static 修饰即可。所以也称为静态变量、静态方法。

在 Java 类中,可用 static 修饰属性、方法、代码块、内部类:

被修饰后的成员具备以下特点:

  • 随着类的加载而加载
  • 优先于对象存在
  • 修饰的成员,被所有对象所共享
  • 访问权限允许时,可不创建对象,直接被类调用

静态变量的特点

静态变量的默认值规则和实例变量一样。

  • 静态变量值是所有对象共享。
  • 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用。
  • 如果权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问,也可以通过“对象.静态变量”的方式访问(但是更推荐使用类名.静态变量的方式)。
  • 静态变量的 get/set 方法也静态的,当局部变量与静态变量重名时,使用“类名.静态变量”进行区分。
public class Employee {private static int total;//这里私有化,在类的外面必须使用 get/set 方法的方式来访问静态变量static String company; //这里缺省权限修饰符,是为了方便类外以“类名.静态变量”的方式访问private int id;private String name;public Employee() {total++;id = total;//这里使用 total 静态变量的值为 id 属性赋值}public Employee(String name) {this();this.name = name;}

静态变量存储在JVM中的元空间(线程共享)。

静态方法

静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。

  • 只要权限修饰符允许,静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用(但是更推荐使用类名.静态方法的方式)。
  • 在 static 方法内部只能访问类的 static 修饰的属性或方法,不能访问类的非 static 的结构。
  • 静态方法可以被子类继承,但不能被子类重写。
  • 静态方法的调用都只看编译时类型。
  • 因为不需要实例就可以访问 static 方法,因此 static 方法内部不能有 this,也不能有super。如果有重名问题,使用“类名.”进行区别。

六、单例(Singleton)设计模式 和 代码块

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。"套路"经典的设计模式共有 23 种。每个设计模式均是特定环境下特定问题的处理方法。

在这里插入图片描述
何为单例模式:

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

实现思路

如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为 private,这样,就不能用 new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。

因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

饿汉式实现方法

class Singleton {// 1.私有化构造器private Singleton() {}// 2.内部提供一个当前类的实例// 4.此实例也必须静态化private static Singleton single = new Singleton();// 3.提供公共的静态的方法,返回当前类的对象public static Singleton getInstance() {return single;}
}

特点:立即加载,即在使用类的时候已经将对象创建完毕。

  • 优点:实现起来简单;没有多线程安全问题。
  • 缺点:当类被加载的时候,会初始化 static 的实例,静态变量被创建并分配内存空间,从这以后,这个 static 的实例便一直占着这块内存,直到类被卸载时,静态变量被摧毁,并释放所占有的内存。因此在某些特定条件下会耗费内存。

懒汉式实现方法

class Singleton {// 1.私有化构造器private Singleton() {}// 2.内部提供一个当前类的实例// 4.此实例也必须静态化private static Singleton single;// 3.提供公共的静态的方法,返回当前类的对象public static Singleton getInstance() {if(single == null) {single = new Singleton();}return single;}
}

特点:延迟加载,即在调用静态方法时实例才被创建。

  • 优点:实现起来比较简单;当类被加载的时候,static 的实例未被创建并分配内存空间,当静态方法第一次被调用时,初始化实例变量,并分配内存,因此在某些特定条件下会节约内存。
  • 缺点:在多线程环境中,这种实现方法是完全错误的,线程不安全,根本不能保证单例的唯一性。(多个线程同时调用Get方法就会创建多个实例。)

代码块:

如果成员变量想要初始化的值不是一个硬编码的常量值,而是需要通过复杂的计算或读取文件、或读取运行环境信息等方式才能获取的一些值,该怎么办呢?此时,可以考虑代码块(或初始化块)。

静态代码块的特点

  • 可以有输出语句。
  • 可以对类的属性、类的声明进行初始化操作。
  • 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  • 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  • 静态代码块的执行要先于非静态代码块。
  • 静态代码块随着类的加载而加载,且只执行一次。

非静态代码块的意义
如果多个重载的构造器有公共代码,并且这些代码都是先于构造器其他代码执行的,那么可以将这部分代码抽取到非静态代码块中,减少冗余代码。

非静态代码块的执行特点

  • 可以有输出语句。
  • 可以对类的属性、类的声明进行初始化操作。
  • 除了调用非静态的结构外,还可以调用静态的变量或方法。
  • 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  • 每次创建对象的时候,都会执行一次。且先于构造器执行。

final 修饰的变量在 Java 中表示不可变(immutable),即一旦被赋值后就不能再修改。如果是常量,则不可修改值,如果是引用类型,则无法更改该变量的引用对象,但可以修改该对象内部的变量。

七、抽象类和抽象方法

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

在这里插入图片描述

我们声明一些几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长。那么这些共同特征应该抽取到一个共同父类:几何图形类中。但是这些方法在父类中又无法给出具体的实现,而是应该交给子类各自具体实现。那么父类在声明这些方法时,就只有方法签名,没有方法体,我们把没有方法体的方法称为抽象方法。Java 语法规定,包含抽象方法的类必须是抽象类。

  • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
  • 抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
  • 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
    理解:子类的构造方法中,有默认的 super()或手动的 super(实参列表),需要访问父类构造方法。
  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

举例:
在航运公司系统中,Vehicle 类需要定义两个方法分别计算运输工具的燃料效率和行驶距离。

问题:卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle 类不能提供计算方法,但子类可以

//Vehicle 是一个抽象类,有两个抽象方法。
public abstract class Vehicle{public abstract double calcFuelEfficiency(); //计算燃料效率的抽象方法public abstract double calcTripDistance(); //计算行驶距离的抽象方法
}
public class Truck extends Vehicle{public double calcFuelEfficiency( ) { //写出计算卡车的燃料效率的具体方法 }public double calcTripDistance( ) { //写出计算卡车行驶距离的具体方法 }
}
public class RiverBarge extends Vehicle{public double calcFuelEfficiency( ) { //写出计算驳船的燃料效率的具体方法 }public double calcTripDistance( ) { //写出计算驳船行驶距离的具体方法}
}

解决方法: Java 允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

  • 当功能内部一部分实现是确定的,另一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
  • 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
abstract class Template {public final void getTime() {long start = System.currentTimeMillis();code();long end = System.currentTimeMillis();System.out.println("执行时间是:" + (end - start));}public abstract void code();
}class SubTemplate extends Template {public void code() {for (int i = 0; i < 10000; i++) {System.out.println(i);}}
}

抽象类用于提供一个设计模板,继承的子类需要重新抽象类中设计好的抽象方法,也能够调用抽象类实现的方法。

八、接口

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的 is-a 关系,而接口实现则是 "能不能"的has-a 关系。

例如:电脑都预留了可以插入 USB 设备的 USB 接口,USB 接口具备基本的数据传输的开启功能和关闭功能。你能不能用 USB 进行连接,或是否具备 USB 通信功能,就看你能否遵循 USB 接口规范

在这里插入图片描述

接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。

在 JDK8.0 之前,接口中只允许出现:
(1)公共的静态的常量:其中 public static final 可以省略
(2)公共的抽象的方法:其中 public abstract 可以省略
理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现

在 JDK8.0 时,接口中允许声明默认方法和静态方法:
(3)公共的默认的方法:其中 public 可以省略,建议保留,但是 default 不能省略
(4)公共的静态的方法:其中 public 可以省略,建议保留,但是 static 不能省略

在 JDK9.0 时,接口又增加了:
(5)私有方法
除此之外,接口中没有构造器,没有初始化块,因为接口中没有成员变量需要动态初始化。


类实现接口(implements)接口不能创建对象,但是可以被类实现(implements ,类似于被继承)。类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements 关键
字。

【修饰符】 class 实现类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写// 重写接口中默认方法【可选】
}
【修饰符】 class 实现类 extends 父类 implements 接口{// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写// 重写接口中默认方法【可选】
}

在这里插入图片描述

  • 如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法。
    默认方法可以选择保留,也可以重写。
    重写时,default 单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了
  • 接口中的静态方法不能被继承也不能被重写

在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方
法有重名的,只需要重写一次。

一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。

定义父接口:
public interface Chargeable {void charge();void in();void out();
}定义子接口:
public interface UsbC extends Chargeable,USB3 {void reverse();
}定义子接口的实现类:
public class TypeCConverter implements UsbC {@Overridepublic void reverse() {System.out.println("正反面都支持");}@Overridepublic void charge() {System.out.println("可充电");}@Overridepublic void in() {System.out.println("接收数据");}@Overridepublic void out() {System.out.println("输出数据");}
}

所有父接口的抽象方法都要重写。方法签名相同的抽象方法只需要实现一次。

接口与实现类构成多态引用
实现类实现接口,类似于子类继承父类,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。通过接口类型的变量调用方法,最终执行的是你 new 的实现类对象实现的方法体。

public class Mouse implements USB3 {@Overridepublic void out() {System.out.println("发送脉冲信号");}@Overridepublic void in() {System.out.println("不接收信号");}
}public class KeyBoard implements USB3{@Overridepublic void in() {System.out.println("不接收信号");}@Overridepublic void out() {System.out.println("发送按键信号");}
}
测试类
public class TestComputer {public static void main(String[] args) {Computer computer = new Computer();USB3 usb = new Mouse();computer.setUsb(usb);usb.start();usb.out();usb.in();usb.stop();System.out.println("--------------------------");usb = new KeyBoard();computer.setUsb(usb);usb.start();usb.out();usb.in();usb.stop();System.out.println("--------------------------");usb = new MobileHDD();computer.setUsb(usb);usb.start();usb.out();usb.in();usb.stop();}
}

接口使用静态成员
接口不能直接创建对象,但是可以通过接口名直接调用接口的静态方法和静态常量。

public class TestUSB3 {public static void main(String[] args) {//通过“接口名.”调用接口的静态方法 (JDK8.0 才能开始使用)USB3.show();//通过“接口名.”直接使用接口的静态常量System.out.println(USB3.MAX_SPEED);}
}
  • 对于接口的静态方法,直接使用“接口名.”进行调用即可,也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
  • 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用,接口不能直接创建对象,只能创建实现类的对象
public class TestMobileHDD {public static void main(String[] args) {//创建实现类对象MobileHDD b = new MobileHDD();//通过实现类对象调用重写的抽象方法,以及接口的默认方法,如果实现类重写了就执行重写的默认方法,如果没有重写,就执行接口中的默认方法b.start();b.in();b.stop();//通过接口名调用接口的静态方法
// MobileHDD.show();
// b.show();Usb3.show();}
}

九、内部类

将一个类 A 定义在另一个类 B 里面,里面的那个类 A 就称为内部类(InnerClass),类 B 则称为外部类(OuterClass)。

为什么要声明内部类

具体来说,当一个事物 A 的内部,还有一个部分需要一个完整的结构 B 进行描述,而这个内部的完整的结构 B 又只为外部事物 A 提供服务,不在其他地方单独使用,那么整个内部的完整结构 B 最好使用内部类。

总的来说,遵循高内聚、低耦合的面向对象开发原则。

在这里插入图片描述

如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。

成员内部类作为类的成员的角色:

  • 和外部类不同,Inner class 还可以声明为 private 或 protected;
  • 可以调用外部类的结构。(注意:在静态内部类中不能使用外部类的非静态成员)
  • Inner class 可以声明为 static 的,但此时就不能再使用外层类的非 static 的成员变量;

成员内部类作为类的角色:

  • 可以在内部定义属性、方法、构造器等结构
  • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
  • 可以声明为 abstract 类 ,因此可以被其它的内部类继承
  • 可以声明为 final 的,表示不能被继承
  • 编译以后生成 OuterClass$InnerClass.class 字节码文件(也适用于局部内部类)

注意点:
• 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
• 成员内部类可以直接使用外部类的所有成员,包括私有的数据
• 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

匿名内部类:

因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。

interface A{void a();
}
public class Test{public static void main(String[] args){new A(){@Overridepublic void a() {System.out.println("aaaa");}}.a();}
}

十、枚举类

枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。

定义枚举类(JDK5.0 之前)

class Season{private final String SEASONNAME;//季节的名称private final String SEASONDESC;//季节的描述private Season(String seasonName,String seasonDesc){this.SEASONNAME = seasonName;this.SEASONDESC = seasonDesc;}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("冬天", "白雪皑皑");@Overridepublic String toString() {return "Season{" +"SEASONNAME='" + SEASONNAME + '\'' +", SEASONDESC='" + SEASONDESC + '\'' +'}';}
}
class SeasonTest{public static void main(String[] args) {System.out.println(Season.AUTUMN);}
}

私有化类的构造器,保证不能在类的外部创建其对象
在类的内部创建枚举类的实例。声明为:public static final ,对外暴露这些常量对象
对象如果有实例变量,应该声明为 private final(建议,不是必须),并在构造器中初始化

定义枚举类(JDK5.0 之后)

【修饰符】 enum 枚举类名{常量对象列表
}
【修饰符】 enum 枚举类名{常量对象列表;对象的实例变量列表;
}举例 1public enum Week {MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;
}
public class TestEnum {public static void main(String[] args) {Season spring = Season.SPRING;System.out.println(spring);}
}
  • 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
  • 列出的实例系统会自动添加 public static final 修饰。
  • 如果常量对象列表后面没有其他代码,那么“;”可以省略,否则不可以省略“;”。
  • 编译器给枚举类默认提供的是 private 的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数
  • 如果枚举类需要的是有参构造,需要手动定义,有参构造的 private 可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
  • 枚举类默认继承的是 java.lang.Enum 类,因此不能再继承其他的类型。
  • JDK5.0 之后 switch,提供支持枚举类型,case 后面可以写枚举常量名,无需添加枚举类作为限定。

enum方式定义的要求和特点

  • 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
  • 列出的实例系统会自动添加 public static final 修饰。
  • 如果常量对象列表后面没有其他代码,那么“;”可以省略,否则不可以省略“;”。
  • 编译器给枚举类默认提供的是 private 的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数
  • 如果枚举类需要的是有参构造,需要手动定义,有参构造的 private 可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
  • 枚举类默认继承的是 java.lang.Enum 类,因此不能再继承其他的类型。
  • JDK5.0 之后 switch,提供支持枚举类型,case 后面可以写枚举常量名,无需添加枚举类作为限定。
public enum Week {MONDAY("星期一"),TUESDAY("星期二"),WEDNESDAY("星期三"),THURSDAY("星期四"),FRIDAY("星期五"),SATURDAY("星期六"),SUNDAY("星期日");private final String description;private Week(String description){this.description = description;}@Overridepublic String toString() {return super.toString() +":"+ description;}
}public class TestWeek {public static void main(String[] args) {Week week = Week.MONDAY;System.out.println(week);switch (week){case MONDAY:System.out.println("怀念周末,困意很浓");break;case TUESDAY:System.out.println("进入学习状态");break;case WEDNESDAY:System.out.println("死撑");break;case THURSDAY:System.out.println("小放松");break;case FRIDAY:System.out.println("又信心满满");break;case SATURDAY:System.out.println("开始盼周末,无心学习");break;case SUNDAY:System.out.println("一觉到下午");break;}}
}

枚举类 常用方法:

  • String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!

  • static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法

  • static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。

  • String name():得到当前枚举常量的名称。建议优先使用 toString()。

  • int ordinal():返回当前枚举常量的次序号,默认从 0 开始

实现接口的枚举类

语法:
//1、枚举类可以像普通的类一样,实现接口,并且可以多个,但要求必须实现里面所有的抽象方法!
enum A implements 接口 1,接口 2{
//抽象方法的实现
}
//2、如果枚举类的常量可以继续重写抽象方法!
enum A implements 接口 1,接口 2{常量名 1(参数){//抽象方法的实现或重写},常量名 2(参数){//抽象方法的实现或重写},//...
}举例:
interface Info{void show();
}
//使用 enum 关键字定义枚举类
enum Season1 implements Info{//1. 创建枚举类中的对象,声明在 enum 枚举类的首位SPRING("春天","春暖花开"){public void show(){System.out.println("春天在哪里?");}},SUMMER("夏天","夏日炎炎"){public void show(){System.out.println("宁静的夏天");}},AUTUMN("秋天","秋高气爽"){public void show(){System.out.println("秋天是用来分手的季节");}},WINTER("冬天","白雪皑皑"){public void show(){System.out.println("2002 年的第一场雪");}};//2. 声明每个对象拥有的属性:private final 修饰private final String SEASON_NAME;private final String SEASON_DESC;//3. 私有化类的构造器private Season1(String seasonName,String seasonDesc){this.SEASON_NAME = seasonName;this.SEASON_DESC = seasonDesc;}public String getSEASON_NAME() {return SEASON_NAME;}public String getSEASON_DESC() {return SEASON_DESC;}
}
http://www.xdnf.cn/news/418807.html

相关文章:

  • 速卖通如何低成本测评,让店铺流量与销量双提升
  • MapReduce基本介绍
  • 原生小程序+springboot+vue医院医患纠纷管理系统的设计与开发(程序+论文+讲解+安装+售后)
  • 内存中的“BANK”
  • 125.在 Vue3 中使用 OpenLayers 实现通过 WebGLVector 的方式添加海量点
  • MapReduce打包运行
  • 基于大模型预测胸椎管狭窄诊疗全流程的研究报告
  • 基于开源AI大模型AI智能名片S2B2C商城小程序的零售结算技术创新研究——以京东AI与香港冯氏零售集团智能结算台为例
  • 深入理解 JVM:StackOverFlow、OOM 与 GC overhead limit exceeded 的本质剖析及 Stack 与 Heap 的差异
  • 逆强化学习IRL在医疗行为模式研究中的应用
  • Three.js模型材质调整与性能优化实战
  • JPG与PDF格式转换器
  • 【论文阅读】Dip-based Deep Embedded Clustering with k-Estimation
  • 如何优化MCU中断响应时间
  • 【Ubuntu】neovim Lazyvim安装与卸载
  • coze平台实现文生视频和图生视频(阿里云版)工作流
  • OpenCV进阶操作:风格迁移以及DNN模块解析
  • 【计算机视觉】OpenCV实战项目:基于OpenCV的车牌识别系统深度解析
  • Kafka、RabbitMQ、RocketMQ的区别
  • 加速AI在k8s上使用GPU卡
  • WPS一旦打开,就会修改默认打开方式,怎么解?
  • 【OpenCV】网络模型推理的简单流程分析(readNetFromONNX、setInput和forward等)
  • React+Webpack 脚手架、前端组件库搭建
  • Ansys 计算刚柔耦合矩阵系数
  • Linux之初见进程
  • 使用光标测量,使用 TDR 测量 pH 和 fF
  • day 24
  • 智能手表整机装配作业指导书(SOP)
  • Vue.js---分支切换与cleanup
  • 第六章 GPIO输入——按键检测