day02【抽象类,接口、代码块、final、单例、枚举】
今日内容(记住语法)
- 抽象类
- 拥有抽象方法的类就是抽象类。
- 抽象方法:是只有方法签名没有方法体,必须用abstract修饰。
- 抽象类本身也要用abstract修饰的,作用是让子类继承,子类一定要重写抽象方法。
- 模板思想,设计模板模式。
- 接口
- 更加彻底的抽象,接口中全部是抽象方法和常量(JDK1.8之后)
- 接口体现的是规范思想,实现接口的类必须重写完接口的全部抽象方法,否则这个类必须是抽象类。
- JDK 1.8之后的接口新增了三个方法。接口不再纯洁。
- 代码块(成员变量,方法,构造器,代码块,内部类)
- final关键字
- final是最终的意思。可以修饰类,变量,和方法。
- 修饰类:类不能被继承。类绝育了。
- 修饰方法:方法不能被重写。
- 修饰变量:变量有且仅能被赋值一次。
- 单例设计模式
- 设计模式,面试必考,经典模式。
- 枚举
- 面向对象的一种特殊类型,做信息分类和信息标志的。
教学目标
-
能够写出抽象类的格式
- abstract class Animal{
- }
-
能够写出抽象方法的格式
- public abstract void run();
- 只有方法签名,没有方法体,必须用abstract修饰。
-
父类抽象方法的意义
- 被子类继承
-
写出定义接口的格式
-
public interface SportMan{
-
}
-
-
写出实现接口的格式
- 修饰符 class 实现类 implements 接口1 , 接口2{
- }
-
说出接口中成员的特点
- JDK 1.8之前全部是抽象方法和常量,其他都没有。
- JDK 1.8之后,有了静态方法,默认方法,JDK 1.9之后有了私有方法。
-
描述final修饰的类的特点
- 类不能被继承
-
描述final修饰的方法的特点
扫描二维码关注公众号,回复: 11313726 查看本文章- 方法不能被重写了
-
描述final修饰的变量的特点
- 变量有且仅能被赋值一次
-
能够写出静态代码块的
- static{
- }
第一章 抽象类
1.1 概述
1.1.1 抽象类引入
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话说,父类可能知道子类应该有哪个功能,但是功能具体怎么实现父类是不清楚的(由子类自己决定),父类完全只需要提供一个没有方法体的方法签名即可,具体实现交给子类自己去实现。我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
-
抽象方法 : 没有方法体的方法。
-
抽象类:包含抽象方法的类。
总结
类创造对象,由对象来调用方法
因为子类重写父类的方法,而不会实现父类的代码。所以父类的代码显得有点冗余,为了简化代码,故用抽象的方法:没有方法,只有方法签名,必须用abstract修饰
|什么是抽象方法?
抽象方法:没有方法体,只有方法签名,必须用abstract修饰。
拥有抽象方法的类必须定义成抽象类。
|抽象类:必须用abstract修饰。
小结:
抽象类:拥有抽象方法类必须定义成抽象类,抽象类必须用abstract修饰。
抽象方法:没有方法体,只有方法签名,必须用abstract修饰。重写父类的抽象方法的过程为具体化
抽象类的作用:为了被子类继承。
小结:
抽象类是为了被子类继承,约束子类要重写抽象方法。
注意:一个类继承了抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
1.2 abstract使用格式
abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。
1.2.1 抽象方法
使用abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
代码举例:
public abstract void run();
1.2.2 抽象类
如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
定义格式:
abstract class 类名字 {
}
代码举例:
public abstract class Animal {
public abstract void run();
}
1.2.3 抽象类的使用
要求:继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。
代码举例:
// 父类,抽象类
abstract class Employee {
private String id;
private String name;
private double salary;
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
// 抽象方法
// 抽象方法必须要放在抽象类中
abstract public void work();
}
// 定义一个子类继承抽象类
class Manager extends Employee {
public Manager() {
}
public Manager(String id, String name, double salary) {
super(id, name, salary);
}
// 2.重写父类的抽象方法
@Override
public void work() {
System.out.println("管理其他人");
}
}
// 定义一个子类继承抽象类
class Cook extends Employee {
public Cook() {
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
@Override
public void work() {
System.out.println("厨师炒菜多加点盐...");
}
}
// 测试类
public class Demo10 {
public static void main(String[] args) {
// 创建抽象类,抽象类不能创建对象
// 假设抽象类让我们创建对象,里面的抽象方法没有方法体,无法执行.所以不让我们创建对象
// Employee e = new Employee();
// e.work();
// 3.创建子类
Manager m = new Manager();
m.work();
Cook c = new Cook("ap002", "库克", 1);
c.work();
}
}
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
1.3 抽象类的特征
抽象类的特征总结起来可以说是 有得有失
有得:抽象类得到了拥有抽象方法的能力。
有失:抽象类失去了创建对象的能力。
其他成员(构造器,实例方法,静态方法等)抽象类都是具备的。
总结
方法的{}内叫方法体
面试题:抽象类是否有构造器,抽象类是否可以创建对象,为什么?答:抽象类作为类一定有构造器,而且抽象类必须有构造器。
提供给子类创建对象调用父类构造器使用的。
抽象类虽然有构造器但是抽象类不能创建对象。
反证法:假如抽象类可以创建对象,Animal a = new Animal();
a.run();//抽象方法不能执行,因为它没有方法体,所以抽象类不能创建对象!
|注意:抽象类除了有得有失,类的其他成分它都具备。
小结:
抽象类不能创建对象。抽象类可以包含抽象方法。
|除此之外,类有的成分,抽象类都具备。|
1.4 抽象类的注意事项
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
-
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
-
抽象类中,可以有构造器,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
-
抽象类存在的意义是为了被子类继承,抽象类体现的是模板思想。
理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。
1.5 抽象类存在的意义
抽象类存在的意义是为了被子类继承,否则抽象类将毫无意义,抽象类体现的是模板思想,模板是通用的东西抽象类中已经是具体的实现(抽象类中可以有成员变量和实现方法),而模板中不能决定的东西定义成抽象方法,让使用模板(继承抽象类的类)的类去重写抽象方法实现需求,这是典型的模板思想。
总结
抽象类存在的意义有2点:
1.抽象类的核心意义就是为了被子类继承(就是为了派生子类),否则抽象类毫无意义!(基本准则)拓展:2.抽象类体现的是模板思想,部分实现,部分抽象。可以设计模板设计模式。
1.6 第一个设计模式:模板模式
我们现在使用抽象类设计一个模板模式的应用,例如在小学的时候,我们经常写作文,通常都是有模板可以套用的。假如我现在需要定义新司机和老司机类,新司机和老司机都有开车功能,开车的步骤都一样,只是驾驶时的姿势有点不同,新司机:开门,点火,双手紧握方向盘,刹车,熄火
,老司机:开门,点火,右手握方向盘左手抽烟,刹车,熄火
。我们可以将固定流程写到父类中,不同的地方就定义成抽象方法,让不同的子类去重写,代码如下:
// 司机开车的模板类
public abstract class Driver {
public void go() {
System.out.println("开门");
System.out.println("点火");
// 开车姿势不确定?定义为抽象方法
ziShi();
System.out.println("刹车");
System.out.println("熄火");
}
public abstract void ziShi();
}
现在定义两个使用模板的司机:
public class NewDriver extends Driver {
@Override
public void ziShi() {
System.out.println("新司机双手紧握方向盘");
}
}
public class OldDriver extends Driver {
@Override
public void ziShi() {
System.out.println("老司机右手握方向盘左手抽烟...");
}
}
编写测试类
public class Demo02 {
public static void main(String[] args) {
NewDriver nd = new NewDriver();
nd.go();
OldDriver od = new OldDriver();
od.go();
}
}
运行效果:自行测试
可以看出,模板模式的优势是,模板已经定义了通用架构,使用者只需要关心自己需要实现的功能即可!非常的强大!
总结
什么是设计模式?
设计模式是前人或者技术大牛或者软件行业在生产实战中发现的优秀软件设计架构和思想。
后来者可以直接用这些架构或者思想就可以设计出优秀,提高开发效率,提高软件可扩展性和可维护性
模板设计模式就是一种经典的设计模式思想。
模板设计模式的作用:优化代码架构,提高代码的复用性,相同功能的重复代码无需反复书写!例如作文模块:
可以做到部分实现,部分抽象,抽象的东西交给使用模板的人重写实现!
例 |标题固定的:《我的爸爸》
第一段固定:
请介绍一下你的爸爸,说说你的爸爸有多好,说说有多牛;
正文部分:
抽象出来。
|结尾部分:
我爸爸真棒,有这样的爸爸简直太爽了,下辈子还要做他儿子
私有的部分只能在本类访问
第二章 接口
2.1 概述
我们已经学完了抽象类,抽象类中可以用抽象方法,也可以有普通方法,已经构造器,成员变量等。那么什么是接口呢?接口是更加彻底的抽象,接口中全部是抽象方法。(JDK8之前),接口同样是不能创建对象的。
2.2 定义格式
//接口的定义格式:
修饰符 interface 接口名称{
// 抽象方法
}
// 修饰符:public|缺省
// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”
2.3 接口成分的特点
在JDK8之前,接口中的成分包含:抽象方法和常量
2.3.1.抽象方法
注意:接口中的抽象方法默认会自动加上public abstract修饰程序员无需自己手写!!
按照规范:以后接口中的抽象方法建议不要写上public abstract。因为没有必要啊,默认会加上。
2.3.2 常量
在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。
2.3.3 案例演示
public interface InterF {
// 抽象方法!
// public abstract void run();
void run();
// public abstract String getName();
String getName();
// public abstract int add(int a , int b);
int add(int a , int b);
// 它的最终写法是:
// public static final int AGE = 12 ;
int AGE = 12; //常量
String SCHOOL_NAME = "黑马程序员";
}
2.4 基本的实现
2.4.1 实现接口的概述
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
总结
类来创建对象,对象来调用方法
接口常量采用有参构造器赋值,然后在定义对象的时候传入要赋值的变量
2.4.2 实现接口的格式
/**接口的实现:
在Java中接口是被实现的,实现接口的类称为实现类。
实现类的格式:*/
[修饰符] class 类名 implements 接口1,接口2,接口3...{
}
从上面格式可以看出,接口是可以被多实现的。大家可以想一想为什么呢?
2.4.3 类实现接口的要求和意义
-
必须重写实现的全部接口中所有抽象方法。
-
如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
-
意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。
2.4.4 类与接口基本实现案例
假如我们定义一个运动员的接口(规范),代码如下:
/**
接口:接口体现的是规范。
* */
public interface SportMan {
void run(); // 抽象方法,跑步。
void law(); // 抽象方法,遵守法律。
String compittion(String project); // 抽象方法,比赛。
}
接下来定义一个乒乓球运动员类,实现接口,实现接口的实现类代码如下:
package com.itheima._03接口的实现;
/**
* 接口的实现:
* 在Java中接口是被实现的,实现接口的类称为实现类。
* 实现类的格式:
* [修饰符] class 类名 implements 接口1,接口2,接口3...{
*
*
* }
* */
public class PingPongMan implements SportMan {
@Override
public void run() {
System.out.println("乒乓球运动员稍微跑一下!!");
}
@Override
public void law() {
System.out.println("乒乓球运动员守法!");
}
@Override
public String compittion(String project) {
return "参加"+project+"得金牌!";
}
}
测试代码:
public class TestMain {
public static void main(String[] args) {
// 创建实现类对象。
PingPongMan zjk = new PingPongMan();
zjk.run();
zjk.law();
System.out.println(zjk.compittion("全球乒乓球比赛"));
}
}
1.4.5 类与接口的多实现案例
类与接口之间的关系是多实现的,一个类可以同时实现多个接口。
首先我们先定义两个接口,代码如下:
/** 法律规范:接口*/
public interface Law {
void rule();
}
/** 这一个运动员的规范:接口*/
public interface SportMan {
void run();
}
然后定义一个实现类:
/**
* Java中接口是可以被多实现的:
* 一个类可以实现多个接口: Law ,SportMan
*
* */
public class JumpMan implements Law ,SportMan {
@Override
public void rule() {
System.out.println("尊长守法");
}
@Override
public void run() {
System.out.println("训练跑步!");
}
}
从上面可以看出类与接口之间是可以多实现的,我们可以理解成实现多个规范,这是合理的。
总结
创建实现类对象调用重写的方法执行
类与类是单继承
一个类实现多个接口,必须重写完全部接口中的全部抽象方法,否则这个类要定义成抽象类
2.5 接口与接口的多继承
Java中,接口与接口之间是可以多继承的:也就是一个接口可以同时继承多个接口。大家一定要注意:
类与接口是实现关系
接口与接口是继承关系
接口继承接口就是把其他接口的抽象方法与本接口进行了合并。
案例演示:
public interface Abc {
void go();
void test();
}
/** 法律规范:接口*/
public interface Law {
void rule();
void test();
}
*
* 总结:
* 接口与类之间是多实现的。
* 接口与接口之间是多继承的。
* */
public interface SportMan extends Law , Abc {
void run();
}
总结
多个接口可以用继承来进行合并
|类与类是单继承关系:一个类只能继承一个直接父类。
类与接口是多实现关系:一个类可以同时实现多个接口。
接口与接口是多继承关系:一个接口可以同时继承多个接口。+
2.6 JDK 8之后的接口新增方法
从JDK 8开始之后,接口不再纯洁了,接口中不再只是抽象方法,接口还可以有默认方法(也就是实例方法),和静态方法了,还包含了私有实例方法和私有静态方法
2.6.1 含有默认方法和静态方法
默认方法:使用 default
修饰,不可省略,供子类调用或者子类重写。
静态方法:使用 static
修饰,供接口直接调用。
代码如下:
public interface InterFaceName {
public default void method() {
// 执行语句
}
public static void method2() {
// 执行语句
}
}
总结
标灰色的代码不是没有用到就是可以省略
接口可以认为是特殊的类
JDK 1.8开始之后接口新增了三个方法:
(1)默认方法:其实就是我们之前写的实例方法。
–必须用default修饰。
–默认会加public修饰。
–只能用接口的实现类的对象来调用–可以直接加static修饰。
《2)静态方法:
–默认会加public修饰。
| --接口的静态方法只能用接口的类名称本身调用,
(3)私有方法:
– 其实就是私有的实例方法,必须加private修饰,只能在本接口中访问,通常是给其他私有方法或者给默认方法调用的!
2.6.2 含有私有方法和私有静态方法
私有方法:使用 private
修饰,供接口中的默认方法或者静态方法调用。
代码如下:
public interface InterFaceName {
private void method() {
// 执行语句
}
}
2.6.3 新增方法的使用
默认方法和静态方法以及私有方法和私有静态方法,遵循面向对象的继承关系使用原则,实现类依然可以访问接口的非私有方法,对于接口中的非私有静态方法,可以直接通过接口名进行访问。
重写默认方法注意(了解):
-
子接口重写默认方法时,default关键字可以保留。
-
实现类重写默认方法时,default关键字不可以保留。
2.7 实现多个接口使用注意事项
2.7.1 多个接口同名静态方法
如果实现了多个接口,多个接口中存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
public interface A {
public static void test(){
}
}
interface B {
public static void test(){
}
}
class C implements A , B{
public static void main(String[] args) {
People.test();
B.test();
// C.test(); // 编译出错
}
}
总结
应使用文档注释即/**
*/
类的父类是亲爹,只能一个(类的继承只能一个)
接口(的实现)可看作是干爹,可以多实现
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中默认方法重名,子类就近选择执行父类的成员方法(亲爹的方法与干爹的方法重名是,子类就近选择亲爹的方法)
1.如果实现了多个接口,多个接口中存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问各自静态方法。
类没有指定继承会默认继承object
3.当一个类实现多个接口时,多个接口中存在同名的默认方法。
实现类必须重写这个方法(废除接口的同名方法,重写子类的方法)
4.接口中,没有构造器,不能创建对象。(重点)
接口是更彻底的抽象,连构造器都没有,自然不能创建对象!
2.7.2 优先级的问题
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。代码如下:
定义接口:
interface A {
public default void methodA(){
System.out.println("AAAAAAAAAAAA");
}
}
定义父类:
class D {
public void methodA(){
System.out.println("DDDDDDDDDDDD");
}
}
定义子类:
class C extends D implements A {
// 未重写methodA方法
}
定义测试类:
public class Test {
public static void main(String[] args) {
C c = new C();
c.methodA();
}
}
输出结果:
DDDDDDDDDDDD
2.8 接口小结
- 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
- 接口中的方法全是抽象方法,默认会自动加上public abstract修饰
- JDK 8开始,接口不再纯洁,支持静态方法,默认方法,私有方法。
- 接口中,没有构造器,不能创建对象。
- 类与接口是多实现的
- 接口与接口是多继承的
- 接口体现的规范。
第三章 代码块
3.1 引入
类的成分:
1.成员变量
2.构造器
3.成员方法
4.代码块
5.内部类
我们已经学完了成员变量,构造器,成员方法,接下来我们来介绍以下代码快,代码块按照有无static可以分为静态代码块和实例代码块。
3.2 静态代码块
静态代码块
必须有static修饰,必须放在类下。与类一起加载执行。
格式
static{
// 执行代码
}
特点:
- 每次执行类,加载类的时候都会先执行静态代码块一次。
- 静态代码块是自动触发执行的,只要程序启动静态代码块就会先执行一次。
- 作用:在启动程序之前可以做资源的初始化,一般用于初始化静态资源。
案例演示
public class DaimaKuaiDemo01 {
public static String sc_name ;
// 1.静态代码块
static {
// 初始化静态资源
sc_name = "黑马程序员!";
System.out.println("静态代码块执行!");
}
public static void main(String[] args) {
System.out.println("main方法执行");
System.out.println(sc_name);
}
}
3.3 实例代码块
实例代码块
没有static修饰,必须放在类下。与对象初始化一起加载。
格式
{
// 执行代码
}
特点:
- 无static修饰。属于对象,与对象的创建一起执行的。
- 每次调用构造器初始化对象,实例代码块都要自动触发执行一次。
- 实例代码块实际上是提取到每一个构造器中去执行的。
- 作用:实例代码块用于初始化对象的资源。
案例演示
public class DaimaKuaiDemo02 {
private String name ;
// 实例代码块。 无static修饰。
{
System.out.println("实例代码块执行");
name = "dl";
}
// 构造器
public DaimaKuaiDemo02(){
//System.out.println("实例代码块执行");
}
// 有参数构造器
public DaimaKuaiDemo02(String name){
//System.out.println("实例代码块执行");
}
public static void main(String[] args) {
// 匿名对象,创建出来没有给变量。
new DaimaKuaiDemo02();
new DaimaKuaiDemo02();
new DaimaKuaiDemo02("xulei");
}
}
// 输出三次:实例代码块执行
常用API
总结
创建对象:new 对象();//为匿名对象
对象 变量名 = new 对象();//创建对象并用变量接收,有对象名,就是变量名
第四章 final关键字
4.1 概述
学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了final
关键字,用于修饰不可改变内容。
- final: 不可改变,最终的含义。可以用于修饰类、方法和变量。
- 类:被修饰的类,不能被继承。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,有且仅能被赋值一次。
4.2 使用方式
4.2.1 修饰类
final修饰的类,不能被继承。
格式如下:
final class 类名 {
}
代码:
final class Fu {
}
// class Zi extends Fu {} // 报错,不能继承final的类
查询API发现像 public final class String
、public final class Math
、public final class Scanner
等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
4.2.2 修饰方法
final修饰的方法,不能被重写。
格式如下:
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
代码:
class Fu2 {
final public void show1() {
System.out.println("Fu2 show1");
}
public void show2() {
System.out.println("Fu2 show2");
}
}
class Zi2 extends Fu2 {
// @Override
// public void show1() {
// System.out.println("Zi2 show1");
// }
@Override
public void show2() {
System.out.println("Zi2 show2");
}
}
4.2.3 修饰变量-局部变量
- 局部变量——基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:
public class FinalDemo1 {
public static void main(String[] args) {
// 声明变量,使用final修饰
final int a;
// 第一次赋值
a = 10;
// 第二次赋值
a = 20; // 报错,不可重新赋值
// 声明变量,直接赋值,使用final修饰
final int b = 10;
// 第二次赋值
b = 20; // 报错,不可重新赋值
}
}
思考,如下两种写法,哪种可以通过编译?
写法1:
final int c = 0;
for (int i = 0; i < 10; i++) {
c = i;
System.out.println(c);
}
写法2:
for (int i = 0; i < 10; i++) {
final int c = i;
System.out.println(c);
}
根据 final
的定义,写法1报错!写法2,为什么通过编译呢?因为每次循环,都是一次新的变量c。这也是大家需要注意的地方。
4.2.4 修饰变量-实例成员变量
成员变量涉及到初始化的问题,初始化方式有显示初始化和构造器初始化,只能选择其中一个:
- 显示初始化(在定义成员变量的时候立马赋值);
public class Student {
final int num = 10;
}
-
构造器初始化(在构造器中赋值一次)。
注意:每个构造器中都要赋值一次!
public class Student {
final int num = 10;
final int num2;
public Student() {
this.num2 = 20;
// this.num2 = 20;
}
public Student(String name) {
this.num2 = 20;
// this.num2 = 20;
}
}
被final修饰的常量名称,一般都有书写规范,所有字母都大写。
总结
目标:fina1修饰静态成员变量。
final修饰变量的总规则:有且仅能被赋值一次。
final修饰静态成员变量,变量变成了常量。
常量:有public static fina1修饰,名称字母全部大写,多个单词用下划线连接。
拓展:
final修饰静态成员变量可以在哪些地方赋值一次:
1.定义的时候赋值一次。
2.可以在静态代码块中赋值一次。
目标:
fina1修饰实例成员变量。(了解。用不到)
final修饰变量的总规则:有且仅能被赋值一次。
拓展:
fina1修饰实例成员变量可以在哪些地方赋值1次:
1.定义的时候赋值一次。
2.可以在实例代码块中赋值一次。
3.在每个构造器中赋值一次。
第五章 单例设计模式
正常情况下一个类可以创建多个对象
public static void main(String[] args) {
// 正常情况下一个类可以创建多个对象
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
}
5.1 单例设计模式的作用
单例模式,是一种常用的软件设计模式。通过单例模式可以保证系统中,应用该模式的这个类只有一个实例。即一个类只有一个对象实例。
总结
目标:单例设计模式。(面试重点,常考)什么是单例?
单例的意思是一个类永远只存在一个对象,不能创建多个对象。
为什么要用单例?
开发中有很多类的对象我们只需要一个,例如虚拟机对象!任务管理器对象!
对象越多越占内存,有些时候只需要一个对象就可以实现业务,单例可以节约内存,提高性能!
如何实现单例?
5.2 单例设计模式实现步骤
-
将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
-
在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
-
定义一个静态方法返回这个唯一对象。
5.3 单例设计模式的类型
根据实例化对象的时机单例设计模式又分为以下两种:
-
饿汉单例设计模式
-
懒汉单例设计模式
5.4 饿汉单例设计模式
饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
代码如下:
public class Singleton {
// 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Singleton() {}
// 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
private static final Singleton instance = new Singleton();
// 3.定义一个静态方法返回这个唯一对象。
public static Singleton getInstance() {
return instance;
}
}
总结
(1)饿汉单例设计模式:
–通过类获取单例对象的时候,对象已经提前做好了!
–实现步骤:
1.定义一个单例类。
2.把类的构造器私有。
3.定义一个静态成员变量用于存储一个对象!
(饿汉单例在返回对象的时候,对象要已经做好,所以这里直接创建出来!)
4.定义一个方法返回单例对象。
5.5 懒汉单例设计模式
懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才例化出对象。不着急,故称为“懒汉模式”。
代码如下:
public class Singleton {
// 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
private static Singleton instance;
// 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Singleton() {}
// 3.定义一个静态方法返回这个唯一对象。要用的时候才例化出对象
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
注意:懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态。我们在学习完多线程的时候还会再讲解如何解决这个问题。
总结
不赋值,不创建对象,只创建变量就为空
(2)懒汉单例设计模式:
–通过类获取单例对象的时候发现没有对象才会去创建一个对象。
–实现步骤:
1.定义一个单例类。
2.把类的构造器私有。
3.定义一个静态成员变量用于存储一个对象!
(懒汉单例不能直接创建对象,必须需要的时候才创建)
4.定义一个方法返回单例对象,判断对象不存在才创建一次,存在直接返回!
5.6 小结
单例模式可以保证系统中一个类只有一个对象实例。
实现单例模式的步骤:
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
第六章 枚举
6.1 不使用枚举存在的问题
假设我们要定义一个人类,人类中包含姓名和性别。通常会将性别定义成字符串类型,效果如下:
public class Person {
private String name;
private String sex;
public Person() {
}
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
// 省略get/set/toString方法
}
public class Demo01 {
public static void main(String[] args) {
Person p1 = new Person("张三", "男");
Person p2 = new Person("张三", "abc"); // 因为性别是字符串,所以我们可以传入任意字符串
}
}
不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全。
总结
常量用类名访问
枚举类的特点:
1.枚举类是fina1修饰的,不能被继承。
2.枚举类默认继承了枚举类型:java.lang.Enum。
3.枚举类的第一行罗列的是枚举类的对象。而且是用常量存储的。
4.所以枚举类的第一行写的都是常量名称,默认存储了枚举对象。
5.枚举类的构造器默认是私有的。
6.枚举类相当于是多例设计模式。
小结:
记住枚举的格式和特点
6.2 枚举的作用与应用场景
枚举的作用:一个方法接收的参数是固定范围之内的时候,那么即可使用枚举。
6.3 枚举的基本语法
6.3.1 枚举的概念
枚举是一种特殊类。枚举是有固定实例个数的类型,我们可以把枚举理解成有固定个数实例的多例模式。
6.3.2 定义枚举的格式
enum 枚举名 {
第一行都是罗列枚举实例,这些枚举实例直接写大写名字即可。
}
总结
目标:枚举类用于做信息标志和信息分类。
常量做信息标志和分类:虽然也挺好,但是入参不受控制,入参太随性无法严谨。
枚举类用于做信息标志和信息分类:优雅!
建议以后做信息标志和信息分类采用枚举进行!|
6.3.3 入门案例
- 定义枚举:BOY表示男,GIRL表示女
enum Sex {
BOY, GIRL; // 男,女
}
- Perosn中的性别有String类型改为Sex枚举类型
public class Person {
private String name;
private Sex sex;
public Person() {
}
public Person(String name, Sex sex) {
this.name = name;
this.sex = sex;
}
// 省略get/set/toString方法
}
- 使用是只能传入枚举中的固定值
public class Demo02 {
public static void main(String[] args) {
Person p1 = new Person("张三", Sex.BOY);
Person p2 = new Person("张三", Sex.GIRL);
Person p3 = new Person("张三", "abc");
}
}
5.3.4 枚举的其他内容
枚举的本质是一个类,我们刚才定义的Sex枚举最终效果如下:
enum Sex {
BOY, GIRL; // 男,女
}
// 枚举的本质是一个类,我们刚才定义的Sex枚举相当于下面的类
final class SEX extends java.lang.Enum<SEX> {
public static final SEX BOY = new SEX();
public static final SEX GIRL = new SEX();
public static SEX[] values();
public static SEX valueOf(java.lang.String);
static {};
}
枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等。
public enum Sex {
BOY(18), GIRL(16);
public int age;
Sex(int age) {
this.age = age;
}
public void showAge() {
System.out.println("年龄是: " + age);
}
}
public class Demo03 {
public static void main(String[] args) {
Person p1 = new Person("张三", Sex.BOY);
Person p2 = new Person("张三", Sex.GIRL);
Sex.BOY.showAge();
Sex.GIRL.showAge();
}
}
运行效果:自行测试
6.4 应用场景
6.5 枚举的应用
枚举的作用:枚举通常可以用于做信息的分类,如性别,方向,季度等。
枚举表示性别:
public enum Sex {
MAIL, FEMAIL;
}
枚举表示方向:
public enum Orientation {
UP, RIGHT, DOWN, LEFT;
}
枚举表示季度
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
6.6 小结
- 枚举类在第一行罗列若干个枚举对象。(多例)
- 第一行都是常量,存储的是枚举类的对象。
- 枚举是不能在外部创建对象的,枚举的构造器默认是私有的。
- 枚举通常用于做信息的标志和分类。