final
/**
* 变量分类:
* 1、成员变量
* 成员变量分为两种: 静态成员变量、实例成员变量
* 1)静态成员变量:static修饰的成员变量,也叫类变量,也叫静态变量
* 2)实例成员变量:非static修饰的成员变量,也叫实例变量,也叫普通变量
* 2、局部变量
* **/
public class Test {
// 4、final修饰静态成员变量,变量的值不能被改变
public static final String name = "xxx";
// name = "mmm"; // 错误,final修饰的变量,值不能被改变
//5、final修饰实例成员变量,变量的值不能被改变
public final int phone = 1698936544;
//phone = 123456; // 错误,final修饰的变量,值不能被改变
public static void main(String[] args) {
//3、final修饰实例成员变量,变量的值不能被改变
final int age = 10;
//age = 20 // 错误,final修饰的变量,值不能被改变
}
// 3、final修饰局部变量
public static void buy(final double z){
// z = 2 // 错误,final修饰的变量,值不能被改变
}
}
// 1、final 修饰类,类不能被继承
final class Person{
}
class Student{
//2、 final修饰方法,方法不能被重写
public final void study(){
}
}
常量
单例类
1、什么是设计模式?
2、单例设计模式
单例类的作用: new一个对象的时候会导致把里面不想要的所有方法和属性都new出来,占用内存,把它设计成单例类就可以避免内存被占用的问题
2.1 饿汉式单例
2.2 懒汉式单例
枚举类
1、认识枚举类
2、枚举类的特点
//用枚举类写一个单例类
public enum A{
X;
}
3、枚举类的应用场景
3.1 应用
以下为使用常量和使用枚举类完成信息标志和分类的对比,体现枚举类的优势
// 需求:模拟上下左右移动图片
// 使用常量实现上面案例
// 常量类
public class Constant {
public static final int UP = 0;
public static final int DOWN = 1;
public static final int LEFT = 2;
public static final int RIGHT = 3;
}
// 应用
public class Application {
public static void main(String[] args) {
// 用常量做信息标志和分类,缺点是参数值不受约束
// 调用方法
move(Constant.UP);//这里会被随意修改也不会报错,如move(5)
}
public static void move(int direction) {
switch (direction) {
case Constant.UP:
System.out.println("向上移动");
break;
case Constant.DOWN:
System.out.println("向下移动");
break;
case Constant.LEFT:
System.out.println("向左移动");
break;
case Constant.RIGHT:
System.out.println("向右移动");
break;
default:
System.out.println("无效的移动方向");
}
}
}
// 使用枚举类实现
// 定义枚举B
public enum B {
// 枚举类的第一行,只能罗列枚举对象名称
UP, DOWN, LEFT, RIGHT;
}
// 应用
public class Application {
public static void main(String[] args) {
// 需求:模拟上下左右移动图片
// 用枚举做信息标志和分类,参数值受枚举类约束
// 调用方法
move(B.UP);//这里会被随意修改也不会报错,如move(5)
}
public static void move(B direction) {
switch (direction) {
case UP:
System.out.println("向上移动");
break;
case DOWN:
System.out.println("向下移动");
break;
case LEFT:
System.out.println("向左移动");
break;
case RIGHT:
System.out.println("向右移动");
break;
default:
System.out.println("无效的移动方向");
}
}
}
抽象类
抽象类的好处
父类的抽象方法必须在子类全部重写,重写的方法在父类只需要一行代码定义,更优雅简洁
模板方法设计模式
案例如下:
//定义了一个父类抽象类Person
public abstract class Person {
public void write()
{
System.out.println("今天天气真好,花儿都开了");
travel();//调用抽象方法
System.out.println("这么好的天气,不出去喝一杯咖啡吗");
}
public abstract void travel(); // 定义抽象方法
}
//定义了子类Student
public class Student extends Person {
// 重写了父类抽象方法
@Override
public void travel() {
System.out.println("小鸟也在唱歌,太阳也出来了");
}
}
// 定义了子类Teacher
public class Teacher extends Person {
// 重写了父类抽象方法
@Override
public void travel() {
System.out.println("老师们都要约好一起出去玩儿了");
}
}
// 应用
public class Test {
public static void main(String[] args) {
/**
* 需求:老师和学生分别写一篇作文,老师的第一段作文和学生的第一段相同,
* 老师的最后一段和学生的最后一段相同,其他的内容都不同
* **/
Teacher teacher = new Teacher();
teacher.write();
Student student = new Student();
student.write();
}
}
接口
1、认识接口(传统接口,JDK8以前的版本)
// 定义一个A接口
public interface A {
// 接口中定义常量public static final可以省略
// public static final int UP = 0;
int UP = 0;
// 接口中定义抽象方法public abstract void move()中,public abstract可以省略
// public abstract void move();
void move();
}
//定义一个B接口
public interface B {
String name = "B";
void run();
}
//定义一个Studnt类,继承A,B接口,student类又被称为实现类
/**
* Student类被成为实现类
* 1、实现类Student,实现多个接口,必须重写全部接口的抽象方法,否则这个类必须定义成抽象类
* **/
public class Student implements A,B{
@Override
public void move() {
System.out.println("student move");
}
@Override
public void run() {
System.out.println("student run");
}
}
// 应用
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.move();
}
}
2、接口的好处
2.1 为什么要用接口?
因为Java类是单继承的,为了弥补类单继承的缺陷,让一个类在继承了一个父类的同时角色更多,功能更强大,因此用到了接口
// 有接口A,B,以及父类Person,那么子类Student继承父类且实现A,B接口的写法如下
// student不仅继承Person,还成为了A,B接口的实现类
**public class Student extends Person implements A,B {
}**
// 实现类:Teacher类
public class Teacher implements A,B{
}
// 应用
public class Test {
public static void main(String[] args) {
// 引用类型, 多态性
Person p = new Student();
A a = new Student();
B b = new Student();
// 接口可以实现面向对象编程,更利于解耦
A aTeacher = new Student(); // 右边的可以以是Teacher
B bTeacher = new Teacher();// 右边的可以以是Student
}
}
2.2 接口练习
// Student类
public class Student {
private String name;
private String sex;
private int score;
public Student() {
}
public Student(String name, String sex, int score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
// 定义一个接口A
public interface A {
void printStudentMsg();
void printAverageScore();
}
// 实现类 imp1
public class imp1 extends Student implements A{
// 定义一个私有引用类型变量存放多个学生数据
private Student[] students;
// 有参构造器,将传递过来的数据赋值给students
public imp1(Student[] students) {
this.students = students;
}
@Override
public void printStudentMsg() {
System.out.println("学生信息如下:");
for (int i = 0; i < students.length; i++) {
System.out.println("姓名:" + students[i].getName() + ",性别:" + students[i].getSex() + ",成绩:" + students[i].getScore());
}
}
@Override
public void printAverageScore() {
// 定义一个变量,用于记录学生成绩的总和
int sum = 0;
for (int i = 0; i < students.length; i++) {
sum += students[i].getScore();
}
System.out.println("平均成绩为:"+ sum / students.length);
}
}
// 实现类 imp2
package com.oop.example3;
public class imp2 extends Student implements A{
// 定义一个私有引用类型变量存放多个学生数据
private Student[] students;
// 有参构造器,将传递过来的数据赋值给students
public imp2(Student[] students) {
this.students = students;
}
@Override
public void printStudentMsg() {
// 打印学生信息,并计算出男生多少个,女士多少个
System.out.println("学生信息如下:");
int male = 0;
int female = 0;
for (int i = 0; i < students.length; i++) {
System.out.println("姓名:" + students[i].getName() + ",性别:" + students[i].getSex() + ",成绩:" + students[i].getScore());
if (students[i].getSex().equals("男")) {
male++;
} else {
female++;
}
}
System.out.println("男生:" + male + ",女士:" + female);
}
@Override
public void printAverageScore() {
// 去掉一个最高分,一个最低分,并计算平均成绩
int max = students[0].getScore();
int min = students[0].getScore();
int sum = 0;
for (int i = 0; i < students.length; i++) {
if (students[i].getScore() > max) {
max = students[i].getScore();
}
if (students[i].getScore() < min) {
min = students[i].getScore();
}
sum += students[i].getScore();
}
System.out.println("去掉一个最高分一个最低分平均成绩为:" + (sum - max - min) / (students.length - 2));
}
}
// 应用
public class Test {
public static void main(String[] args) {
Student[] allstudents = new Student[10];
allstudents[0] = new Student("小明", "男", 100);
allstudents[1] = new Student("小红", "女", 90);
allstudents[2] = new Student("小刚", "男", 80);
allstudents[3] = new Student("小花", "女", 70);
allstudents[4] = new Student("小李", "男", 95);
allstudents[5] = new Student("小赵", "男", 78);
allstudents[6] = new Student("小钱", "女", 90);
allstudents[7] = new Student("小孙", "男", 80);
allstudents[8] = new Student("李四", "男", 80);
allstudents[9] = new Student("王二", "男", 60);
// new一个实现类imp1,指向接口A
A imp1 = new imp2(allstudents ); // 父类引用指向子类对象,并传递参数allstudents
imp1.printStudentMsg();
imp1.printAverageScore();
}
}
3、JDK8开始,接口新增的3种方法
3.1、默认方法(实例方法):使用default修饰,默认加public修饰。
3.2、**私有方法:**必须用private修饰(JDK 9才开始支持)
3.3、类方法(静态方法):使用static修饰,默认会被加上public修饰,只能用接口名来调用
3.4、接口注意事项
1、接口与接口可以多继承:一个接口可以同时继承多个接口
/**
* 类与类:单继承,一个雷只能继承一个直接父类
* 类与接口:多实现,一个类可以同时实现多个接口
* 接口与接口:多继承,一个接口可以同时继承多个接口
**/
interface A{
void a();
}
interface B{
void b();
}
interface C extends A,B{
void c();
}
class D implements C{
@Override
public void a() {
}
@Override
public void b() {
}
@Override
public void c() {
}
}
2、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现。
不冲突:A1和B1虽然都有show方法,但返回类型都是相同的void,因此不会冲突
冲突:方法签名冲突,A1和B1都有show方法,但是返回类型不一致导致冲突,如果将A1的show返回值类型改为String,或者将B1的返回值类型改为void类型就不会冲突
3、一个类继承了父类,又同时实现了接口,如果父类中和接口中有同名的方法,实现类会优先用父类的。
4、一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可
抽象类和接口的区别
综合案例
需求:某智能家居系统,可以让用户选择要控制的家用设备(吊灯,电视机,洗衣机),并可以对它们进行打开或者关闭操作
// 目标:面向对象编程实现智能家居控制系统。
// 角色:设备(吊灯,电视机,洗衣机)
// 具备的功能:开和关。
// 谁控制他们:智能控制系统(单例对象),控制调用设备的开和关
// 1、设计接口Switch ,功能是控制开和关
public interface Switch {
void press();
}
// 2、设计一个家电父类JD,并实现Switch 接口
public class JD implements Switch {
private String name;
private Boolean status;
public JD() {
}
public JD(String name, Boolean status) {
this.name = name;
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
@Override
public void press() {
status = !status;
}
}
// 3、创建TV,Lamp,WashMachine子类,继承JD类。
// 只写了TV类,其他类都相同写法
public class TV extends JD{
public TV(String name, Boolean status) {
super(name, status);
}
}
// 4、创建智能控制系统(单例对象),控制调用设备的开和关
public class SmartHomeCtrol {
//智能控制系统类,设计成单例类 start
private static final SmartHomeCtrol smartHomeCtrol= new SmartHomeCtrol();
private SmartHomeCtrol(){}
public static SmartHomeCtrol getInstance(){
return smartHomeCtrol;
}
//智能控制系统类,设计成单例类end
// 控制开关调用方法
public void control(JD jd){
//打印家电目前状态
System.out.println(jd.getName() + "状态目前是:" + (jd.getStatus()?"开着":"关着"));
System.out.println("请开始你的操作。。。");
jd.press();
System.out.println(jd.getName() + "操作后的状态是:" + (jd.getStatus()?"开着":"关着"));
}
public void printAllStatus(JD[] jds){
// 用for循环,索引的方法打印所有的家电状态
for(int i=0;i<jds.length;i++){
JD jd = jds[i];
System.out.println((i+1)+"-"+jd.getName() + "状态目前是:" + (jd.getStatus()?"开着":"关着"));
}
}
}
// 应用
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
JD[] jds = new JD[3];
jds[0] = new TV("TCL电视",true);
jds[1] = new WashMachine("美的洗衣机",false);
jds[2] = new Lamp("雷士灯泡",true);
// 创建一个对象,控制开关
SmartHomeCtrol s = SmartHomeCtrol.getInstance(); // 调用单列类的方法,得到唯一的一个对象
// s.control(jds[0]);
while (true) {
s.printAllStatus(jds);
// 让用户输入数字,根据数字控制开关
System.out.println("请输入数字:");
Scanner sc = new Scanner(System.in);
String num = sc.next();
switch (num) {
case "1":
s.control(jds[0]);
break;
case "2":
s.control(jds[1]);
break;
case "3":
s.control(jds[2]);
break;
case "exit":
System.out.println("退出APP");
return;
default:
System.out.println("输入错误");
}
}
}
}