三、语法
1.包
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护。
- 建包的语法格式:package 公司域名倒写.技术名称。包名建议全部英文小写,且具备意义。
package com.javase.demo;
public class Test {
}
- 建包语句必须在第一行,一般IDEA工具会帮助创建。
- 设置IDEA自动导包:
File -> Settings -> Editor -> General -> Auto Import -> Java -> Add unambiguous imports on the fly
导包
- 相同包下的类可以直接访问,不同包下的类必须导包才可以使用!导报格式:import 包名.类名;
- 假如一个类中需要用到不同类,而这两个类的名称一样,默认只能导入一个类,另一个类要带包名访问。
package com.javase.packagedemo1;
// 导包
import com.javase.packagedemo1.it.Student;
// 导包
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
// 目标:理解以下两点
// 1.同一个包下的类 互相可以直接访问
System.out.println(User.onlineNumber);
// 2.不同包下的类 必须先导包才能访问。
Student student = new Student();
Scanner scanner = new Scanner(System.in);
// 3.如果这个类中使用不同包下的相同的类名 此时默认只能导入一个类的包 另外一个类要使用全名访问
com.javase.packagedemo1.it1.Student student1 = new com.javase.packagedemo1.it1.Student();
}
}
2.权限修饰符
什么是权限修饰符?
- 权限修饰符:是用来控制一个成员能够被访问大的范围。
- 可以修饰成员变量、方法、构造器、内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
权限修饰符的分类和具体作用范围:
- 权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected -> public)。
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
学完权限修饰符需要具备如下能力:
- 能够识别别人定义的成员的访问范围。
- 自己定义成员(方法、成员变量、构造器等)一般需要满足如下要求:
- 成员变量一般私有。
- 方法一般公开。
- 如果该成员只希望本类访问,使用private修饰。
- 如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。
3.final
final的作用:
- final关键字是最终的意思,可以修饰(类、方法、变量)。
- 修饰类:表明该类是最终类,不能被继承。
- 修饰方法:表明该方法是最终方法,不能被重写。
- 修饰变量:表明该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
final修饰变量的注意:
- final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
- final修饰的变量是引用类型:那么变量存储的地址值不能发送改变,但是地址指向的对象内容是可以发送变化的。
public class Test {
public static void main(String[] args) {
final Cat cat = new Cat(100);
System.out.println(cat.getWeight());
cat.setWeight(20);
System.out.println(cat.getWeight());
}
}
4.常量
a.常量概述和基本作用
常量:
- 常量是使用了public static final修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。
- 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
public class Constant {
public static final String SCHOOL_NAME = "长留山大学";
public static final String LOGIN_NAME = "花千骨";
public static final String PASS_WORD = "123456";
}
常量命名规范:英文的单词全部大写,多个单词下划线连接起来。
常量的执行原理:
- 在编译阶段会进行”宏替换“,把使用常量的地方全部替换成真实的字面量。
- 这样做的好处就是使用常量的程序的执行性能与直接使用字面量是一样的。
b.常量做信息标志和分类
案例说明:
- 现在开发的超级玛丽游戏需要接收用户输入的四个方向的信息(上下左右),以便控制人物移动的方向。
public class ConstantDemo {
public static final int UP = 1;
public static final int DOWN = 2;
public static final int LEFT = 3;
public static final int RIGHT = 4;
public static void main(String[] args) {
move(UP);
}
public static void move(int flag) {
switch (flag) {
case UP:
System.out.println("向上");
break;
case DOWN:
System.out.println("向下");
break;
case LEFT:
System.out.println("向左");
break;
case RIGHT:
System.out.println("向右");
break;
}
}
}
选择常量做信息标志和分类:
- 代码可读性好, 实现了软编码形式。
5.枚举
a.枚举的概述
枚举:
- 枚举是Java中一种特殊类型。
- 枚举的作用:是为了做信息的标志和信息的分类。
定义枚举类的格式:
修饰符 enum 枚举名称 {
第一行都是罗列枚举类实例的名称。
}
enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
反编译后观察枚举的特征:
Season.java
/**
* 枚举类
*/
public enum Season {
// 枚举的第一行必须罗列枚举类的对象名称 建议全部大写
SPRING, SUMMER, AUTUMN, WINTER;
}
先javac编译再javap反编译
$ javac Season.java
$ javap Season.class
Compiled from "Season.java"
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
public static Season valueOf(java.lang.String);
static {};
}
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
public static Season valueOf(java.lang.String);
static {};
}
枚举的特征:
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 枚举类的构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称。
- 枚举相当于是多例模式。
b.枚举的使用场景演示
案例说明:
- 现在开发的超级玛丽游戏需要接收用户输入的四个方向的信息(上下左右),以便控制人物移动的方向。
选择常量做信息标志和分类:
- 虽然可以实现可读性,但是入参值不受约束,代码相对不够严谨。
枚举做信息标志和分类:
- 代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用!
import com.javase.enumdemo.Orientation;
public class ConstantDemo {
public static void main(String[] args) {
move(UP);
}
public static void move(Orientation orientation) {
switch (orientation) {
case UP:
System.out.println("向上");
break;
case DOWN:
System.out.println("向下");
break;
case LEFT:
System.out.println("向左");
break;
case RIGHT:
System.out.println("向右");
break;
}
}
}
/**
* 枚举类
*/
public enum Season {
// 枚举的第一行必须罗列枚举类的对象名称 建议全部大写
SPRING, SUMMER, AUTUMN, WINTER;
}
6.抽象类
a.抽象类
抽象类:
- 在Java中abstact是抽象的意思,可以修饰类、成员方法。
- abstract修饰类,这个类就是抽象类。
- abstract修饰方法,这个方法就是抽象方法。
修饰符 abstract class 类名 {
修饰符 abstract 返回值类型 方法名称(形参列表);
}
/**
* 抽象类
*/
public abstract class Animal {
/**
* 抽象方法
*/
public abstract void run();
}
注意事项:
- 抽象方法只有方法签名,不能声明方法体。
- 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错。
抽象的使用场景:
- 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。
- 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
Animal.java
/**
* 抽象类
*/
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 抽象方法
*/
public abstract void run();
}
Dog.java
public class Dog extends Animal {
@Override
public void run() {
System.out.println("狗跑的很快!");
}
}
Test.java
public class Test {
public static void main(String[] args) {
// 目标:先认识抽象类 再了解它的使用场景
Dog dog = new Dog();
dog.run();
}
}
总结:
- 抽象类、抽象方法是什么样的?
- 都是用abstract修饰的,抽象方法只有方法签名,不能写方法体。
- 一个类中定义了抽象方法,这个类必须声明成抽象类。
- 抽象类基本作用是什么?
- 作为父类,用来被继承的。
- 继承抽象类有哪些要注意?
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
b.抽象类的应用案例
需求:
- 某加油站推出了2中支付卡,一种是预存10000的金卡,后续加油享受8折优惠,另外一种是预存5000的银卡,后续加油享受8.5折优惠。
- 请分别实现2种卡片进入收银系统后的逻辑,卡片需要包含持卡人名称、余额、支付功能。
分析实现:
- 创建一张卡片父类:定义属性包括持卡人名称、余额、支付功能(具体实现交给子类)。
- 创建一张金卡类:重写支付功能,按照原价的8折计算输出。
- 创建一张银卡类:重写支付功能,按照原价的8.5折计算输出。
Card.java
/**
* 抽象卡片类
*/
public abstract class Card {
// 持卡人
private String cardholder;
// 卡余额
private double balance;
public String getCardholder() {
return cardholder;
}
public void setCardholder(String cardholder) {
this.cardholder = cardholder;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
/**
* 支付
*
* @param money 支付金额
*/
public abstract void pay(double money);
}
GoldCard.java
public class GoldCard extends Card {
/**
* 支付
*
* @param money 支付金额
*/
@Override
public void pay(double money) {
System.out.println("您当前消费: " + money + "元.");
System.out.println("您卡片当前余额是: " + getBalance());
// 计算折扣价
double specialPrice = money * 0.8;
System.out.println("您实际支付:" + specialPrice + "元.");
// 更新账户余额
setBalance(getBalance() - specialPrice);
System.out.println("结算后,您卡片余额为" + getBalance() + "元.");
}
}
SilverCard.java
public class SilverCard extends Card {
/**
* 支付
*
* @param money 支付金额
*/
@Override
public void pay(double money) {
System.out.println("您当前消费: " + money + "元.");
System.out.println("您卡片当前余额是: " + getBalance());
// 计算折扣价
double specialPrice = money * 0.85;
System.out.println("您实际支付:" + specialPrice + "元.");
// 更新账户余额
setBalance(getBalance() - specialPrice);
System.out.println("结算后,您卡片余额为" + getBalance() + "元.");
}
}
Test.java
public class Test {
public static void main(String[] args) {
// 目标:学习抽象类的基本使用 做父类 被继承 重写抽象方法
// 金卡
System.out.println("-----------金卡-----------");
GoldCard goldCard = new GoldCard();
goldCard.setBalance(10000);
goldCard.setCardholder("白子画");
goldCard.pay(2000);
// 银卡
System.out.println("-----------银卡-----------");
SilverCard silverCard = new SilverCard();
silverCard.setBalance(5000);
silverCard.setCardholder("笙箫默");
silverCard.pay(2000);
}
}
当父类知道子类一定要完成某个行为,但每个子类完成的情况又不同,就可以把方法定义成抽象方法,具体方法交给子类重写来完成。这样既可以约束子类必须做这件事情,又可以提高代码的复用性。
c.抽象类的特征、注意事项
特征和注意事项:
- 类有的成员(成员变量、方法、构造器)抽象类都具备。
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
- 不能用abstract修饰变量、代码块、构造器。
- 最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)。
public abstract class Animal {
public abstract void run();
}
// 反证法:假如抽象类可以创建对象
Animal animal = new Animal();
// 该方法的方法体都没有 因此抽象类不能创建对象
animal.run();
// 就算抽象类中没有抽象方法也不能创建对象 Java是一种严谨的编程语言
final和abstract是什么关系?
- 互斥关系。final修饰类不能被继承,abstract修饰抽象类必须被继承才有意义。
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
- 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
d.抽象类的应用知识:模板方式模式
什么时候使用模板方法模式?
- 使用场景说明:当系统种出现同一个功能多出在开发,而该功能种大部分代码是一样的,只有其中部分可能不同的时候。
模板方法模式实现步骤:
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中定义通用且能确定的代码。
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现。
Ⅰ.案例
需求:
- 现在又两类学生,一类是中学生,一类是小学生,他们都要写《我的区长父亲》的作文。
- 要求每种类型的学生,标题第一段和最后一段,内容必须一样。正文部门自由发挥。
- 请选择最优的面向对象方法进行设计。
Student.java
/**
* 学生类
*/
public abstract class Student {
/**
* 作文模板方法
*/
public void write() {
// 开头
System.out.println("《我的区长父亲》");
System.out.println("xx xxx xxx xxx xxx xxx xx");
// 正文
System.out.println(writeMain());
// 结尾
System.out.println("xx xxx xxx xxx xxx xxx xx");
System.out.println("感谢我的区长父亲。");
}
public abstract String writeMain();
}
PrimarySchoolStudent.java
/**
* 小学生类
*/
public class PrimarySchoolStudent extends Student {
/**
* 写正文
*
* @return 正文
*/
@Override
public String writeMain() {
return "xxx区长,xxx区长,xxx区长,我现在读小学。";
}
}
MiddleSchoolStudent.java
/**
* 中学生类
*/
public class MiddleSchoolStudent extends Student {
/**
* 写正文
*
* @return 正文
*/
@Override
public String writeMain() {
return "xxx区长,xxx区长,xxx区长,我现在读中学。";
}
}
Test.java
public class Test {
public static void main(String[] args) {
// 目标:理解模板方法模式的思想和使用步骤
// 创建小学生对象
PrimarySchoolStudent primarySchoolStudent = new PrimarySchoolStudent();
primarySchoolStudent.write();
// 创建中学生对象
MiddleSchoolStudent middleSchoolStudent = new MiddleSchoolStudent();
middleSchoolStudent.write();
}
}
模板方法是建议使用final修饰,这样会更专业,为什么?
- 模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板法规范,则模板法规范就失效了,因此加上final后可以防止子类重写了模板方法,这样更安全、更专业。
/**
* 学生类
*/
public abstract class Student {
/**
* 作文模板方法
*/
public final void write() {
// 开头
System.out.println("《我的区长父亲》");
System.out.println("xx xxx xxx xxx xxx xxx xx");
// 正文
System.out.println(writeMain());
// 结尾
System.out.println("xx xxx xxx xxx xxx xxx xx");
System.out.println("感谢我的区长父亲。");
}
public abstract String writeMain();
}
总结:
- 模板方法模式解决了什么问题?
- 提高了代码的复用性。
- 模板方式已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现,因此,使用者只需要关心自己需要实现的功能即可。
7.接口
a.接口概述、特点
接口的定义与特点:
- 接口的格式如下:
// 接口用关键字interface来定义
public interface 接口名 {
// 常量
// 抽象方法
}
- JDK8之前接口中只能是抽象方法和常量,没有其他成分。(注意:接口中常量一定要初始化赋值。)
package com.javase.interfacedemo;
/**
* 声明一个接口:体现一种规范 规范一定是公开的
* 里面的常量或抽象方法及时不写权限修饰符 也都是公开的 public
*/
public interface InterfaceDemo {
// 目标:接口中的成分特点 JDK8之前接口中只能有抽象方法和常量
// 常量
// public static final String SCHOOL_NAME = "xx大学";
String SCHOOL_NAME = "xx大学";
// 抽象方法
// 注意 由于接口体现规范思想 规范默认都是公开的 所有代码层面 public abstract可以省略不写
// public abstract void run();
// public abstract void eat();
void run();
void eat();
}
什么是接口?
- 接口也是一种规范(约定只能做什么事情)。
- 例如:生活中的电插头插座规范。
b.接口的基本使用:被实现
接口的用法:
- 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3, ... {
}
实现的关键字: implements
- 接口可以被类单实现,也可以被类多实现。
Athlete.java
/**
* 运动员:接口
*/
public interface Athlete {
/**
* 跑步
*/
void run();
/**
* 比赛
*/
void competition();
}
TableTennisPlayer.java
/**
* 乒乓球运动员:实现类
*/
public class TableTennisPlayer implements Athlete {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TableTennisPlayer() {
}
public TableTennisPlayer(String name) {
this.name = name;
}
/**
* 跑步
*/
@Override
public void run() {
System.out.println(name + "开始跑步训练!");
}
/**
* 比赛
*/
@Override
public void competition() {
System.out.println(name + "开始比赛!");
}
}
Test.java
public class Test {
public static void main(String[] args) {
// 目标:理解接口的基本使用:被类实现
TableTennisPlayer tableTennisPlayer = new TableTennisPlayer("东方彧卿");
tableTennisPlayer.run();
tableTennisPlayer.competition();
}
}
接口实现的注意事项:
- 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
c.接口与接口的关系:多继承
基础小结:
- 类和类的关系:单继承。
- 类和接口的关系:多实现。
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
People.java
/**
* 人:接口
*/
public interface People {
/**
* 吃饭
*/
void eat();
/**
* 睡觉
*/
void sleep();
}
Law.java
/**
* 法律:接口
*/
public interface Law {
/**
* 遵守法律
*/
void rule();
}
Athlete.java
/**
* 运动员:接口
*
* 接口可以同时继承多个接口
*/
public interface Athlete extends People, Law{
/**
* 跑步
*/
void run();
/**
* 比赛
*/
void competition();
}
BasketballPlayer.java
public class BasketballPlayer implements Athlete {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BasketballPlayer() {
}
public BasketballPlayer(String name) {
this.name = name;
}
/**
* 跑步
*/
@Override
public void run() {
System.out.println("跑步");
}
/**
* 比赛
*/
@Override
public void competition() {
System.out.println("比赛");
}
/**
* 遵守法律
*/
@Override
public void rule() {
System.out.println("遵守法律");
}
/**
* 吃饭
*/
@Override
public void eat() {
System.out.println("吃饭");
}
/**
* 睡觉
*/
@Override
public void sleep() {
System.out.println("睡觉");
}
}
Test.java
public class Test {
public static void main(String[] args) {
// 目标:理解接口多继承的作用
BasketballPlayer basketballPlayer = new BasketballPlayer("白子画");
basketballPlayer.run();
basketballPlayer.competition();
basketballPlayer.rule();
basketballPlayer.eat();
basketballPlayer.sleep();
}
}
接口多继承的作用:
- 规范合并,整合多个接口为同一个接口,便于子类实现。
d.JDK8开始接口新增方法
从JDK8版本开始后,Java只对接口的成员方法进行了新增,原因如下:
某个项目Version1.0成功上线
public interface Inter {}
public class InterImplA implements Inter {}
public class InterImplB implements Inter {}
该项目Version2.0需要对Inter接口丰富,加入10个新的抽象方法,此时改了接口就要所有实现类实现这些方法。(一旦有了抽象方法,实现类一定要重写全部抽象方法。)
public interface Inter {
//新增若干抽象方法
}
public class InterImplA implements Inter {}
public class InterImplB implements Inter {}
如何能在丰富接口功能的同时又不对子类代码进行更改呢?
- 允许接口中直接定义带有方法体的方法
第一种:默认方法
- 类似之前写的普通实例方法:必须用defautl修饰。
- 默认会public修饰,需要用接口的实现类的对象来调用。
default void run() {
System.out.println("---开始跑---");
}
第二种:静态方法
- 默认会public修饰,必须static修饰。
- 注意:接口的静态方法必须用本身的接口名来调用。
static void inAddr() {
System.out.println("xxx xxx xxx");
}
第三种:私有方法
- 就是私有的实例方法:必须使用private修饰,从JDK1.9才开始有的。
- 只能在本类中被其他的默认方法或者私有方法访问。
private void go() {
System.out.println("---准备---");
}
Athlete.java
package com.javase.interfacejdk8;
public interface Athlete {
/**
* 1.JDK8开始 默认方法(实例方法) 必须default修饰 默认用的public修饰
* 默认方法:接口不能创建对象 这个方法只能过继给实现类 由实现类的对象调用
*/
default void run() {
System.out.println("跑步");
// 3.私有方法(实例方法)
sleep();
}
/**
* 2.静态方法
* 必须使用static修饰 默认用public修饰
* 接口的静态方法 必须接口自己调用
* Java新增方法的语法 Java源码自己会用到
*/
static void eat() {
System.out.println("吃饭");
}
/**
* 3.私有方法(实例方法)
* JDK 1.9开始才支持
* 必须在接口内部才能被访问
*/
private void sleep() {
System.out.println("睡觉");
}
}
class TableTennisPlayer implements Athlete {
}
class Test {
public static void main(String[] args) {
TableTennisPlayer tableTennisPlayer = new TableTennisPlayer();
// 1.默认方法
tableTennisPlayer.run();
// 2.静态方法
Athlete.eat();
// tableTennisPlayer.eat();
}
}
IDEA切换模块的编译版本:File -> Project Structure -> Project Settings -> Modules -> Sources -> Language level
总结:
- JDK8开始后新增了哪些方法?
- 默认方法:default修饰,实现类对象调用。
- 静态方法:static修饰,必须用当前接口名调用。
- 私有方法:private修饰,JDK9才开始有的,只能在接口内部被调用。
JDK8新增的3中方法我们自己在开发中很少使用,通常是Java源码涉及到。现阶段需要理解、识别语法、明白调用关系即可。
e.使用接口的注意事项
- 接口不能创建对象
- 接口更加彻底的抽象
- 一个类实现多个接口,多个接口中有同样的静态方法不冲突。
- 接口的静态方法不能C调用,只能A调用A的,只能B调用
- 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
- 就近原则,必须先继承父类再继承接口。
class Cat extends Animal implements Food {
}
- 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
- 一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
interface A {
int run();
}
interface B {
void run();
}
// 该种情况不能多继承 因接口A和接口B中的 run()方法不同 一个有返回值 一个没有返回值
interface C extends A, B {
}