学习视频:https://www.bilibili.com/video/BV1Cv411372m
如侵权,请私信联系本人删除
文章目录
- 黑马程序员Java教程学习笔记(四)
- static:修饰成员变量、内存机制
- static:修饰成员方法、内存机制
- static访问注意事项
- static:应用知识-工具类
- static:应用知识-代码块
- static:应用知识-单例模式
- 继承:概述、案例
- 继承:特点、访问特点、方法重写
- 继承:构造器特点
- 语法:包、权限修饰符
- 语法:final的语法
- 语法:常量、常量在开发中的作用
- 语法:枚举、枚举的作用
- 抽象类:概述、案例、特点
- 抽象类:模板方法模式
- 接口:概述、多实现、多继承
- 接口新增方法、注意事项
- 多态的概述,优势,类型转换问题
- 多态综合案例
- 内部类
- 匿名内部类
- 常用API-Object、Objects
- 常用API-StringBuilder
- 常用API-Math、System、BigDecimal
黑马程序员Java教程学习笔记(四)
static:修饰成员变量、内存机制
static是什么?
- static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改
成员变量可以分为一下两类
第一类:静态成员变量(有static修饰,属于类,内存中加载一次):常表示如在线人数等信息,可以被共享访问。
public class User{
// 静态成员变量
public static String onlineNumber = 161;
}
两种访问方法:
类名.静态成员变量 (推荐)
对象.静态成员变量 (不推荐)
package com.mochu.d1_static;
public class User {
/**
* 在线人数
* static修饰成员变量、静态成员变量、在内存中只有一份,可以共享
*/
public static int onlineNumber = 161;
}
package com.mochu.d1_static;
public class StaticFieldDemo1 {
public static void main(String[] args) {
// 理解static修饰成员变量和访问特点
// 类名.静态成员变量
System.out.println(User.onlineNumber);
// 对象.静态成员变量
User u = new User();
System.out.println(u.onlineNumber);
}
}
第二类:实例成员变量(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。
只能用以下这种方法访问
对象.实例成员变量
package com.mochu.d1_static;
public class User {
/**
* 在线人数
* static修饰成员变量、静态成员变量、在内存中只有一份,可以共享
*/
public static int onlineNumber = 161;
/**
* 实例成员变量,无static修饰,属于每个对象,必须用对象名.访问
*/
private int age;
private String name;
public static void main(String[] args) {
User u = new User();
u.name = "mochu7";
u.age = 21;
System.out.println(u.name);
System.out.println(u.age);
// 同一个类中访问静态成员变量可以不用加类名
System.out.println(onlineNumber);
}
}
Static修饰成员变量的内存原理
static:修饰成员方法、内存机制
成员方法的分类:
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
- 实例成员方法(无static修饰,归属于对象),只能用对象触发访问。
使用场景:
- 表示对像自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
- 如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
Static修饰成员方法的内存原理
static访问注意事项
- 静态方法只能访问静态的成员,不可以直接访问实例成员。
- 实例方法可以访问静态的成员,也可以访问实例成员。
- 静态方法中是不可以出现
this
关键字的。
package com.mochu.d1_static;
public class Test {
// 静态成员
public static int onlineNumber = 10;
public static void test2() {
System.out.println("==test2==");
}
// 实例成员
private String name;
public void run() {
System.out.println(name + "好好学习");
}
// 1. 静态方法只能访问静态成员,不能直接访问实例成员(可以创建对象来访问)
public static void test() {
System.out.println(Test.onlineNumber);
System.out.println(onlineNumber);
Test.test2();
test2();
// System.out.println(name); // 不能直接访问实例成员
// run(); // 不能直接访问实例成员
}
// 2. 实例方法可以访问静态成员,也可以直接访问实例成员
public void go() {
System.out.println(Test.onlineNumber);
System.out.println(onlineNumber);
System.out.println(name);
test();
run();
System.out.println(this);
}
// 3. 静态方法中不可以出现this关键字
public static void test3() {
// this只能代表当前对象,而静态方法可以不用对象调用
// System.out.println(this);
}
public static void main(String[] args) {
}
}
static:应用知识-工具类
工具类是什么?
- 类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用。
package com.mochu.d2_static_util;
import java.util.Random;
public class Login {
public static void main(String[] args) {
System.out.println("验证码:" + mochuUtil.createVerifyCode(6));
}
}
package com.mochu.d2_static_util;
import java.util.Random;
public class mochuUtil {
// 由于工具类无需创建对象,所以将构造器私有
private mochuUtil() {
}
public static String createVerifyCode(int n) {
// 生成验证码
String code = "";
String allString = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random rd = new Random();
for (int i = 0; i < n; i++) {
int index = rd.nextInt(allString.length());
code += allString.charAt(index);
}
return code;
}
}
package com.mochu.d2_static_util;
public class ArrayUtil {
// 私有构造器
private ArrayUtil() {
}
/**
* 工具方法:静态方法
* 功能:展示数组内容
*/
public static String toString(int[] arr) {
if(arr == null) {
return null;
}
// 拼接内容返回
String result = "[";
for (int i = 0; i < arr.length; i++) {
result += (arr.length - 1 == i ? arr[i] : arr[i] + ", ");
}
result += "]";
return result;
}
/**
* 工具方法:静态方法
* 功能:统计平均数
*/
public static double getAverage(double[] arr) {
double max = arr[0];
double min = arr[0];
double sum = 0;
double averageNum = 0;
for (int i = 0; i < arr.length; i++) {
if(max < arr[i]) {
max = arr[i];
}
if(min > arr[i]) {
min = arr[i];
}
sum += arr[i];
}
System.out.println(max);
System.out.println(min);
averageNum = (sum - (max + min)) / (arr.length - 2);
return averageNum;
}
}
package com.mochu.d2_static_util;
public class TestDemo {
public static void main(String[] args) {
int[] arr1 = null;
int[] arr2 = {};
int[] arr3 = {12, 34, 56, 78, 90};
System.out.println(ArrayUtil.toString(arr1));
System.out.println(ArrayUtil.toString(arr2));
System.out.println(ArrayUtil.toString(arr3));
double[] arr4 = {56.8, 77.9, 69.2, 89.5, 72.9, 94.7};
System.out.println("平均分:" + ArrayUtil.getAverage(arr4));
}
}
static:应用知识-代码块
代码块概述
- 代码块是类的五大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
- 在Java类下,使用{}括起来的代码被称为代码块。
代码块分为:
- 静态代码块:
– 格式:static{}
– 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
– 使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用
package com.mochu.d3_static_code;
import java.util.ArrayList;
public class StaticCode {
public static String name;
public static ArrayList<String> cards = new ArrayList<>();
public static void main(String[] args) {
System.out.println("main方法被执行了");
System.out.println(name);
System.out.println(cards);
}
/**
* 静态代码块,属于类、与类一起优先加载、自动触发执行
* 作用:初始化静态资源
*/
static{
System.out.println("静态代码块被执行了");
name = "mochu7";
cards.add("3");
cards.add("4");
}
}
- 构造代码块(实例代码块)(了解,见得少):
– 格式:{}
– 特点:每次创建对象,调用构造器执行时,都会执行该代码块的代码,并且在构造器执行前执行
– 使用场景:初始化实例资源
package com.mochu.d3_static_code;
public class StaticCode1 {
private String name;
public StaticCode1() {
System.out.println("无参构造器被执行");
}
/**
* 构造代码块(实例代码块),无static修饰,属于对象,用于初始化实例资源
*/
{
name = "mochu7";
System.out.println("构造代码块被执行");
}
public static void main(String[] args) {
StaticCode1 test = new StaticCode1();
System.out.println(test.name);
StaticCode1 test1 = new StaticCode1();
System.out.println(test1.name);
}
}
静态代码块应用案例
package com.mochu.d3_static_code;
import java.util.ArrayList;
public class StaticTest3 {
/**
* 定义一个静态集合,这个集合只加载一次
*/
public static ArrayList<String> cards = new ArrayList<String>();
// 在程序运行前,初始化54张牌
static{
// 定义一个数组存储点数
String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
// 定义一个数组存储花色
String[] colors = {"♠", "♦", "♣", "♥"};
// 组合点数花色
for (int i = 0; i < sizes.length; i++) {
for (int j = 0; j < colors.length; j++) {
String card = sizes[i] + colors[j];
cards.add(card);
}
}
cards.add("小王");
cards.add("大王");
}
public static void main(String[] args) {
System.out.println(cards);
}
}
static:应用知识-单例模式
饿汉单例模式
饿汉单例的实现步骤:
- 定义一个类,把构造器私有
- 定义一个静态变量存储一个对象
package com.mochu.d4_static_singleinstance;
/**
* 使用饿汉单例类
* 饿汉单例是在获取对象前,对象已经提前准备好了一个。这个对象只能是一个,所以定义静态成员变量记住。
*/
public class SingelInstance {
public static SingelInstance instance = new SingelInstance();
private SingelInstance() {
}
}
package com.mochu.d4_static_singleinstance;
public class Test {
public static void main(String[] args) {
SingelInstance ss1 = SingelInstance.instance;
SingelInstance ss2 = SingelInstance.instance;
System.out.println(ss1 == ss2);
}
}
懒汉单例模式
package com.mochu.d4_static_singleinstance;
public class SingelInstance2 {
// 定义一个静态成员变量存储一个对象,只加载一次,在内存中只有一份
private static SingelInstance2 instance2; // 这里最好私有化,防止被外部直接调用
//私有化构造器
private SingelInstance2(){
}
//提供一个方法,对外返回一个单例对象
public static SingelInstance2 getInstance() {
if(instance2 == null) {
// 第一次调用对象,创建对象返回
instance2 = new SingelInstance2();
}
return instance2;
}
}
package com.mochu.d4_static_singleinstance;
public class Test2 {
public static void main(String[] args) {
SingelInstance2 ss1 = SingelInstance2.getInstance();
SingelInstance2 ss2 = SingelInstance2.getInstance();
System.out.println(ss1 == ss2);
}
}
继承:概述、案例
什么是继承?
- Java中提供一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系
- 作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了。
- 优点:提高代码复用性、减少代码冗余、增强类的功能扩展性。
继承的设计规范、内存运行原理
继承设计规范:
- 子类们相同特征(共性属性、共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类中。
package com.mochu.d5_extends;
public class People {
private String name;
private int age;
/**
* 共有的行为
*/
public void queryCourse() {
System.out.println(name + "在查看课表");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.mochu.d5_extends;
public class Student extends People{
/**
* 独有的行为
*/
public void feedBack() {
System.out.println(getName() + "写下了反馈");
}
}
package com.mochu.d5_extends;
public class Test {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("mochu7");
stu.setAge(20);
System.out.println(stu.getName());
System.out.println(stu.getAge());
stu.queryCourse();
stu.feedBack();
}
}
继承:特点、访问特点、方法重写
继承的特点:
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器
- Java是单继承模式:一个类只能继承一个直接父类
- Java不支持多继承,但是支持多层继承
- Java中所有的类都是
Object
类的子类
子类是否可以继承父类的静态成员?
- 子类可以直接使用父类的静态成员(通过共享访问),但并非继承
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
如果子类中,出现了重名成员,会优先使用子类的,此时如果一定要在子类中使用父类的成员可以通过super
关键字,指定访问父类成员。如果是指定访问当前对象的成员,而局部有重名成员,则可以使用this
关键字。
package com.mochu.d5_extends;
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
d.showName();
}
}
class Animal {
public String name = "Animal name";
public void run() {
System.out.println("Animal run~");
}
}
class Dog extends Animal {
public String name = "dog";
public void lookDoor() {
System.out.println("dog can look door");
}
public void showName() {
String name = "局部名";
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
run();
super.run();
}
public void run() {
System.out.println("dog run");
}
}
方法重写
什么是方法重写?
- 在继承体系中,子类出现了和父类中一摸一样的方法声明,就称子类的这个方法是重写方法。
方法重写的应用场景
- 当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
- 子类可以重写父类中的方法。
package com.mochu.d6_override;
public class Test {
public static void main(String[] args) {
NewPhone np = new NewPhone();
np.call();
np.sendMessage();
}
}
// 父类
class Phone{
public void call() {
System.out.println("打电话");
}
public void sendMessage() {
System.out.println("发短信");
}
}
// 子类
class NewPhone extends Phone{
// 重写的方法
@Override
public void call() {
super.call();
System.out.println("开始视频通话");
}
@Override
public void sendMessage() {
super.sendMessage();
System.out.println("发有趣的图片");
}
}
@Override
重写注解
@Override
是放在重写后的方法上,作为重写是否正确的校验注解
优点:
- 重写校验注解,加上之后如果出现重写错误,编译阶段会出现错误提示
- 提高代码可读性
方法重写注意事项和要求
- 重写方法的名称,形参列表必须与重写方法的名称和参数列表一致。
- 私有方法不能被重写。
- 子类重写父类方法时,访问权限必须大于或者等于父类(
缺省 < protected < public
) - 子类不能重写父类的静态方法
继承:构造器特点
子类继承父类后构造器的特点:
- 子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。
- 子类再初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
- 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
- 子类构造器的第一行默认都是:
super()
,不写也存在。
package com.mochu.d7_extends_construct;
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
System.out.println(d);
System.out.println("-------------");
Dog d1 = new Dog("金毛");
System.out.println(d1);
}
}
package com.mochu.d7_extends_construct;
public class Animal {
public Animal() {
System.out.println("父类的无参数构造器被执行了");
}
}
package com.mochu.d7_extends_construct;
public class Dog extends Animal{
public Dog(){
System.out.println("子类的无参数构造器被执行了");
}
public Dog(String name) {
System.out.println("子类的有参数构造器被执行了");
}
}
子类构造器访问父类的有参数构造器
package com.mochu.d7_extends_construct;
public class Test {
public static void main(String[] args) {
Teacher t = new Teacher("mochu7", 20);
System.out.println(t.getName());
System.out.println(t.getAge());
}
}
package com.mochu.d7_extends_construct;
public class People {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.mochu.d7_extends_construct;
public class Teacher extends People{
public Teacher(String name, int age) {
// 调用父类的有参数构造器
super(name, age);
}
}
如果父类中没有无参数构造器,只有有参构造器,会报错,因为子类默认是调用父类无参构造器的。可以通过子类构造器中书写super(...)
,手动调用父类的有参构造器。
this
和super
的使用总结
理解
this(...)
访问本类构造器的用法
package com.mochu.d7_extends_construct;
public class Test {
public static void main(String[] args) {
Student s = new Student("mochu7", "家里蹲大学");
System.out.println(s.getName() + "\t" + s.getSchoolName());
Student s1 = new Student("Slevin");
System.out.println(s1.getName() + "\t" + s1.getSchoolName());
}
}
package com.mochu.d7_extends_construct;
public class Student {
private String name;
private String schoolName;
// 如果学生不填写学校名称,默认学校名称为黑马培训中心
public Student(String name){
// 借用有参数构造器
this(name, "黑马培训中心");
}
public Student(String name, String schoolName) {
this.name = name;
this.schoolName = schoolName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
}
语法:包、权限修饰符
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹,建包有利于程序的管理和维护。
- 建包的语法格式:package公司域名倒写.技术名称。报名建议全部英文小写,且具备意义。
- 建包语句必须在第一行,一般IDEA自动创建
导包
- 相同包下的类可以直接访问,不同包下的类必须导包才可以使用,导包格式:
import 包名.类名
- 如果这个类使用不同包下的相同的类名,此时默认只能导入一个类的包,另一个类需要使用全名访问。
什么是权限修饰符?
- 权限修饰符:是用来控制一个成员能够被访问的范围。
- 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
语法:final的语法
final
的作用
final
关键字是最终的意思,可以修饰(类、方法、变量)- 修饰类:表示该类是最终类,不能被继承。
- 修饰方法:表明该方法是最终方法,不能被重写。
- 修饰变量:表明该变量第一次赋值后,不能再次被赋值(有且仅能赋值一次)。
final
修饰变量注意事项:
final
修饰的变量是基本类型:那么变量存储的数据值不能发生改变。final
修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。
package com.mochu.d3_final;
public class Test {
/**
* final修饰静态成员变量(也成为: 常量)
* @param args
*/
public static final String userName = "mochu7";
/**
* final修饰实例成员变量,几乎不用,会导致每个对象都是一样的值且不能修改
* @param args
*/
private final String name = "Slevin";
public static void main(String[] args) {
// 变量有两种:
// 局部变量
// 成员变量:静态成员变量、实例成员变量
// final修饰局部变量
final double pi = 3.14;
// pi = 3.15
buy(10);
Test t = new Test();
System.out.println(t.name);
// final修饰引用类型的变量,地址值不能发生改变,但是地址指向的对象内容可以改变。
final Teacher tch = new Teacher("学习、吹牛逼、打游戏");
// tch = null;
System.out.println(tch.getHobby());
tch.setHobby("运动");
System.out.println(tch.getHobby());
}
public static void buy(final double price) {
// price = 11;
System.out.println(price);
}
}
class Teacher {
private String hobby;
public Teacher(String s) {
this.hobby = hobby;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
final修饰方法,该方法为最终方法,不能被重写
//class People{
// public final void eat() {
// System.out.println("--eat---");
// }
//}
//
//class Student extends People {
// @Override
// public void eat() {
// System.out.println("Student eat");
// }
//}
final修饰类,该类不能被继承
//final class Animal{
//
//}
//class Wolf extends Animal {
//
//}
语法:常量、常量在开发中的作用
常量:
- 常量是使用了
public static final
修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。 - 常量命名规范:英文单词全部大写,多个单词下划线连接起来。
常量的执行原理:
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
package com.mochu.d4_constant;
public class Test {
public static final String USER_NAME = "mochu7";
public static final String PASS_WORD = "mochu777";
public static void main(String[] args) {
System.out.println(USER_NAME);
System.out.println(PASS_WORD);
}
}
语法:枚举、枚举的作用
枚举的概述
- 枚举是Java的一种特殊类型
- 枚举的作用:“是为了做信息的标志和信息的分类”
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称
}
enum Season{
SPRING, SUMMER, AUTUMN, WINTER;
}
将该Java文件编译后反编译
PS C:\Users\Administrator\Downloads> javac .\Season.java
PS C:\Users\Administrator\Downloads> javap .\Season.class
Compiled from "Season.java"
public final class com.mochu.d5_enum.Season extends java.lang.Enum<com.mochu.d5_enum.Season> {
public static final com.mochu.d5_enum.Season SPRING;
public static final com.mochu.d5_enum.Season SUMMER;
public static final com.mochu.d5_enum.Season AUTUMN;
public static final com.mochu.d5_enum.Season WINTER;
public static com.mochu.d5_enum.Season[] values();
public static com.mochu.d5_enum.Season valueOf(java.lang.String);
static {};
}
PS C:\Users\Administrator\Downloads>
反编译后观察特征
枚举的特征:
- 枚举类都是继承了枚举类型:
java.lang.Enum
- 枚举类都是最终类,不可以被继承
- 构造器的构造器都是私有的,枚举对外不能创建对象
- 枚举类的第一行默认都是罗列枚举对象的名称的
- 枚举相当于多例模式
抽象类:概述、案例、特点
抽象类
- 在java中
abstract
是抽象的意思,可以修饰类,成员方法。 abstract
修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表);
}
public abstract class Animal{
public abstract void run();
}
注意事项
- 抽象方法只有方法签名,不能申明方法体
- 一个类中如果定义了抽象方法,这个类必须声明抽象类,否则报错
抽象的使用场景:
- 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。
- 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
package com.mochu.d6_abstract;
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
d.run();
}
}
package com.mochu.d6_abstract;
/**
* 抽象类
*/
public abstract class Animal {
/**
* 抽象方法: 有abstract修饰,不能写方法体
*/
public abstract void run();
}
package com.mochu.d6_abstract;
public class Dog extends Animal{
@Override
public void run() {
System.out.println("dog run");
}
}
package com.mochu.d7_abstract_demo;
public class Test {
public static void main(String[] args) {
GoldCard gc = new GoldCard();
gc.setMoney(10000);
gc.setUserName("mochu7");
gc.pay(300);
System.out.println("您的余额还有:" + gc.getMoney());
}
}
package com.mochu.d7_abstract_demo;
public abstract class Card {
private String userName;
private double money;
/**
* 定义一个抽象方法
* @return
*/
public abstract void pay(double money);
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
package com.mochu.d7_abstract_demo;
public class GoldCard extends Card{
@Override
public void pay(double money) {
System.out.println("您当前消费:" + money);
System.out.println("您当前余额:" + getMoney());
// 优惠价计算
double price = money * 0.8;
System.out.println(getUserName() + "您实际需要支付:" + price);
// 更新账户余额
setMoney(getMoney() - price);
}
}
抽象类的特征和注意事项:
- 类有的成员(成员变量,方法,构造器)抽象类都具备
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类,不然报错
- 不能用
abstract
修饰变量、代码块、构造器 - 得到了抽象方法,就失去了创建对象的能力,抽象类不能创建对象
final
和abstract
是互斥关系
抽象类:模板方法模式
模板方法模式使用场景:
- 当系统中出现同一个功能多出在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
模板方法模式实现步骤:
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方式中只定义通用且能确定的代码。
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现
package d9_abstract_template;
public class Test {
public static void main(String[] args) {
MiddleStudent ms = new MiddleStudent();
ms.write();
System.out.println("-------------");
JuniorStudent js = new JuniorStudent();
js.write();
}
}
package d9_abstract_template;
public abstract class Student {
/**
* 声明了模板方法模式
* @param args
*/
public final void write() {
System.out.println("\t\t\t《我的爸爸》");
// 正文部分,每个子类都不一样,因此把正文部分定义为抽象方法
System.out.println(writeMain());
System.out.println("末尾部分");
}
public abstract String writeMain();
}
package d9_abstract_template;
public class JuniorStudent extends Student{
@Override
public String writeMain() {
return "小学生正文部分";
}
}
package d9_abstract_template;
public class MiddleStudent extends Student{
@Override
public String writeMain() {
return "中学生正文部分";
}
}
接口:概述、多实现、多继承
接口的定义与特点:
- 接口用关键字
interface
来定义 - 接口是一种规范
jdk 1.8
之前,接口中只能有抽象方法和常量- 接口可以被单类实现,也可以被多类实现
public interface 接口名 {
// 常量
// 抽象方法
}
/**
* 声明了一个接口:体现了一种规范,规范一定是公开的
*/
public interface InterfaceDemo {
// jdk 1.8之前,接口中只能有抽象方法和常量
// // 注意:由于接口体现规范思想,规范默认都是公开的,所以代码层面,public static final可以省略不写。
// public static final String USER_NAME = "MOCHU7";
String USER_NAME = "MOCHU7";
// 抽象方法
// 注意:由于接口体现规范思想,规范默认都是公开的,所以代码层面,public abstract可以省略不写。
// public abstract void run();
void run();
// public abstract void eat();
void eat();
}
接口是用来被类实现(implements)
的,实现接口的类称为实现类
。实现类可以理解成所谓的子类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3, ...{
}
实现关键字:implements
package com.mochu.d11_interface_implement;
public class Test {
public static void main(String[] args) {
// 理解接口的基本作用:被类实现
PingPangMan ppm = new PingPangMan("马龙");
ppm.run();
ppm.competition();
ppm.rule();
}
}
package com.mochu.d11_interface_implement;
public class PingPangMan implements SportMan, Law{
private String name;
public PingPangMan(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + "必须跑步");
}
@Override
public void competition() {
System.out.println(name + "必须比赛");
}
@Override
public void rule() {
System.out.println(name + "必须遵纪守法");
}
}
package com.mochu.d11_interface_implement;
public interface SportMan {
void run();
void competition();
}
package com.mochu.d11_interface_implement;
public interface Law {
void rule();
}
接口实现的注意事项:
- 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
- 类和类的关系:单继承
- 类和接口的关系:多实现
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
- 接口多继承的作用:规范合并,整合多个接口为同一个接口,便于子类实现
package com.mochu.d12_interface_extends;
// 接口可以多继承,一个接口可以继承多个接口
public interface SportMan extends Law, People{
void run();
void competition();
}
package com.mochu.d12_interface_extends;
public interface Law {
void rule();
}
package com.mochu.d12_interface_extends;
public interface People {
void eat();
void sleep();
}
接口新增方法、注意事项
JKD8版本开始后,Java只对接口的成员方法进行了新增:允许接口中直接定义带有方法体的方法
第一种:默认方法
- 类似之前的普通实例方法:必须用
default
修饰 - 默认会public修饰。需要用接口的实现类的对象来调用
default void run() {
System.out.println("---开始润---");
}
第二种方法:
- 默认会
public
修饰,必须static
修饰。 - 注意:接口的静态方法必须用本身的接口名来调用
static void inAddr() {
System.out.println("xxx");
}
第三种方法:私有方法
- 就是私有的实例方法:必须使用
private
修饰,从JDK1.9
开始有的。 - 只能在本类中被其他的默认方法或者私有方法访问。
private void go() {
System.out.println("xxx");
}
package com.mochu.d13_interface_jdk8;
public interface SportMan {
/**
* 默认方法:
* JDK8开始新增了默认方法(实例方法),必须default修饰,默认public修饰
* 接口不能创建对象,这个方法只能由实现类对象调用
*/
default public void run(){
System.out.println("润~");
// 内部调用work()放啊
work();
}
/**
* 静态方法:
* 必须使用static修饰,默认使用public修饰
* 接口的静态方法,必须接口名调用
*/
static public void study() {
System.out.println("学~");
}
/**
* 私有方法(实例方法):
* JDK9开始新增的,必须在接口内部才能被访问
*/
private void work() {
System.out.println("做~");
}
}
class PingPongMan implements SportMan {
}
class Test {
public static void main(String[] args) {
PingPongMan ppm = new PingPongMan();
ppm.run();
SportMan.study();
}
}
- 接口不能创建对象(接口更加彻底的抽象)
- 一个类实现多个接口,多个接口中有同样的静态方法不冲突。
- 一个类继承了父类,同时又实现了接口,父类和接口中有同名方法,默认用父类的。
- 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
- 一个接口继承多个接口,是没有问题的,如果多个接口存在规范冲突则不能多继承。
多态的概述,优势,类型转换问题
什么是多态?
- 同类型的对象,执行同一个行为,会表现出不同的行为特征。
多态的常见形式:
父类类型 对象名称 = new 子类构造器
接口 对象名称 = new 实现类构造器
多态成员的访问特点:
- 方法调用:编译看左边,运行看右边
- 变量调用:编译看左边,运行也看左边(多态侧重行为多态)
多态的前提:
- 有继承/实现关系;有父类引用指向子类对象;有方法重写
package com.mochu.d1_ploymorphic;
public abstract class Animal {
public String name = "父类动物";
public abstract void run();
}
package com.mochu.d1_ploymorphic;
public class Dog extends Animal{
public String name = "子类狗";
@Override
public void run() {
System.out.println("dog run");
}
}
package com.mochu.d1_ploymorphic;
public class Turtle extends Animal{
public String name = "子类乌龟";
@Override
public void run() {
System.out.println("Turtle run");
}
}
package com.mochu.d1_ploymorphic;
public class Test {
public static void main(String[] args) {
// 多态的形式:父类类型 对象名称 = new 子类构造器
Animal a1 = new Dog();
// 对于方法调用,运行看作,调用看右,子类调用
a1.run();
// 对于变量调用,运行看左,调用也看左,父类调用
System.out.println(a1.name);
Animal a2 = new Turtle();
a2.run();
System.out.println(a2.name);
}
}
多态的优势
优势:在多态的形势下,右边对象可以实现解耦合,便于扩展和维护。
定义方法的时候,使用父类类型作为参数,该方法就可以接受这个父类的一切子类对象,体现出多态的扩展性与便利。
package com.mochu.d1_ploymorphic;
public class Test {
public static void main(String[] args) {
Animal d = new Dog();
go(d);
Animal t = new Turtle();
go(t);
}
public static void go(Animal a){
System.out.println("开始");
a.run();
System.out.println("结束");
}
}
多态下会产生一个问题:多态下不能使用子类的独有功能,只能使用通用功能
多态下引用数据类型的类型转换
自动化类型转换(从子到父):子类对象赋值给父类类型的变量指向。
强制类型转换(从父到子类):
- 此时必须进行强制类型转换:
子类 对象变量 = (子类)父类类型的变量
- 作用:可以解决多态下的劣势,可以实现调用子类独有的功能
- 注意:如果
转型后的类型
和对象真实类型
不是同一种类型,那么在转换的时候就会出现ClassCastException
package com.mochu.d1_ploymorphic;
public class Turtle extends Animal{
public String name = "子类乌龟";
@Override
public void run() {
System.out.println("Turtle run");
}
// 独有功能
public void layEggs() {
System.out.println("Turtle lay eggs");
}
}
package com.mochu.d1_ploymorphic;
public class Test {
public static void main(String[] args) {
Animal d = new Dog();
go(d);
Animal t = new Turtle();
go(t);
// 强制类型转换,从父类类型到子类类型必须强制转换
Turtle t2 = (Turtle) t;
t2.layEggs();
}
Java建议强制转换前使用instanceof
判断当前对象的真是类型,再进行强制转换
变量名 instanceof 真实类型
判断关键字左边的变量指向的对象的真实类型,是否是左边的类型或者是其子类类型,是则返回true,反之false
package com.mochu.d1_ploymorphic;
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
a.run();
Animal a2 = new Turtle();
a2.run();
if(a2 instanceof Turtle){
Turtle t = (Turtle) a2;
t.layEggs();
}else if(a2 instanceof Dog){
Dog d = new Dog();
d.lookDoor();
}
System.out.println("-----------");
go(new Turtle());
go(new Dog());
}
public static void go(Animal a) {
a.run();
// a到底是哪个对象
if(a instanceof Turtle){
Turtle t = (Turtle) a;
t.layEggs();
}else if(a instanceof Dog){
Dog d = new Dog();
d.lookDoor();
}
}
}
多态综合案例
package com.mochu.d4_ploymorphic_test;
public class Test {
public static void main(String[] args) {
Computer c = new Computer("AlienWare");
c.run();
USB u = new KeyBoard("青轴键盘");
c.installUSB(u);
System.out.println("----------------");
USB u1 = new Mouse("逻辑鼠标");
c.installUSB(u1);
}
}
package com.mochu.d4_ploymorphic_test;
/**
* USB接口
*/
public interface USB {
void connect();
void disconnect();
}
package com.mochu.d4_ploymorphic_test;
public class Computer {
public String name;
public Computer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 提供安装USB的入口
*/
public void installUSB(USB usb) {
usb.connect();
if(usb instanceof KeyBoard) {
KeyBoard k = (KeyBoard) usb;
k.keyDown();
}else if(usb instanceof Mouse) {
Mouse m = (Mouse) usb;
m.dbClick();
}
usb.disconnect();
}
public void run(){
System.out.println(name + "开机了~");
}
}
package com.mochu.d4_ploymorphic_test;
public class KeyBoard implements USB{
public String name;
public KeyBoard(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功插入电脑");
}
@Override
public void disconnect() {
System.out.println(name + "成功拔出电脑");
}
/**
* 独有功能
*/
public void keyDown() {
System.out.println(name + "进行了打字");
}
}
package com.mochu.d4_ploymorphic_test;
public class Mouse implements USB{
public String name;
public Mouse(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功插入电脑");
}
@Override
public void disconnect() {
System.out.println(name + "成功拔出电脑");
}
/**
* 独有功能
*/
public void dbClick() {
System.out.println(name + "进行了双击");
}
}
内部类
- 内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。
public class People{
// 内部类
public class Heart{
}
}
内部类的使用场景、作用:
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。
- 内部类通常可以方便的访问外部类的成员,包括私有的成员。
- 内部类提供了更好的封装性,内部类本身就可以用
private protected
等修饰,封装性可以做更多控制。
内部类的分类:
- 静态内部类
- 成员内部类(非静态内部类)
- 局部内部类
- 匿名内部类
静态内部类
- 有
static
修饰,属于外部类本身。 - 它的特点和使用与普通类完全一样,类有的成分它都有,只是位置在外部类中
public class Outer{
// 静态成员内部类
public static class Inner{
}
}
静态内部类创建对象的格式:外部类名.内部类名 = new 外部类名.内部类构造器;
package com.mochu.d5_innerclass_static;
public class Outer {
public static int a = 100;
private String hobby;
// 静态内部类
public static class Inner{
private String name;
private int age;
public static String schoolName;
public Inner() {
}
public Inner(String name, int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println("名称:" + name);
System.out.println(a);
// System.out.println(hobby);
// Outer o = new Outer();
// System.out.println(o.hobby);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static String getSchoolName() {
return schoolName;
}
public static void setSchoolName(String schoolName) {
Inner.schoolName = schoolName;
}
}
}
package com.mochu.d5_innerclass_static;
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.setName("mochu7");
in.show();
}
}
成员内部类
- 无
static
修饰,属于外部类对象 JDK16
之前,成员内部类不能定义静态成员,JDK16
开始也可以定义静态成员了。
public class Outer{
// 成员内部类
public class Inner{
}
}
成员内部类创建对象的格式:外部类名.内部类名 = new 外部类构造器.new 内部类构造器();
package com.mochu.d6_innerclass;
// 外部类
public class Outer {
// 成员内部类
public class Inner{
private String name;
private int age;
public static int a = 100; // JDK16开始支持静态成员
public static void test(){
System.out.println(a);
}
public void show() {
System.out.println("名称:" + name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static int getA() {
return a;
}
public static void setA(int a) {
Inner.a = a;
}
}
}
package com.mochu.d6_innerclass;
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.setName("mochu7");
in.show();
Outer.Inner.test();
}
}
package com.mochu.d6_innerclass;
public class Test {
public static void main(String[] args) {
People.Heart heart = new People().new Heart();
heart.show();
}
}
class People {
private int heartbeat = 150;
public class Heart{
private int heartbeat = 110;
public void show(){
int heartbeat = 78;
System.out.println(heartbeat); // 要输出78
System.out.println(this.heartbeat); // 要输出110
// 成员类中访问外部类对象:外部类名.this.xxx
System.out.println(People.this.heartbeat); // 要输出150
}
}
}
局部内部类
局部内部类放在方法、代码块、构造器等执行体中。
局部内部类文件名为:外部类$N内部类.class
package com.mochu.d6_innerclass;
public class Test {
public static void main(String[] args) {
class Cat{
private String name;
public static int onLineNumber = 100;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat c = new Cat();
c.setName("叮当猫");
}
}
实用意义不大
匿名内部类
匿名内部类:
- 本质上是一个没有名字的局部内部类,定义在方法中欧给,代码块中等。
- 作用:方便创建子类,最终目的为了简化代码编写。
new 类|抽象类名|或者接口名() {
重写方法;
}
Employee a = new Employee() {
public void work() {
}
};
a.work();
package com.mochu.d6_innerclass;
public class Test {
public static void main(String[] args) {
Animal a = new Animal(){
@Override
public void run(){
System.out.println("老虎跑得快");
}
};
a.run();
}
}
//class Tiger extends Animal{
// @Override
// public void run() {
// System.out.println("老虎跑得很快");
// }
//}
//
abstract class Animal{
public abstract void run();
}
特点总结:
- 匿名内部类是一个没有名字的内部类
- 匿名内部类写出来就会产生一个匿名内部类的对象
- 匿名内部类的对象类型相当于是当前new的那个的类型的子类型
匿名内部类也会产生class
文件
案例:
package com.mochu.d8_innerclass_anoymous;
public class Test {
public static void main(String[] args) {
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("游的很快");
}
};
go(s);
System.out.println("--------------");
Swimming s1 = new Swimming() {
@Override
public void swim() {
System.out.println("老师游的很快");
}
};
go(s1);
System.out.println("----------------");
// 简化,作为方法的入参
go(new Swimming() {
@Override
public void swim() {
System.out.println("游的很快");
}
});
}
public static void go(Swimming s) {
System.out.println("开始");
s.swim();
System.out.println("结束");
}
}
interface Swimming {
void swim();
}
package com.mochu.d8_innerclass_anoymous;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.jar.JarEntry;
/**
* 通过GUI编程,理解匿名类的真实使用场景
*/
public class Test3 {
public static void main(String[] args) {
// 创建窗口
JFrame win = new JFrame("登录窗口");
JPanel panel = new JPanel();
win.add(panel);
// 创建按钮对象
JButton btn = new JButton("登录");
// // 匿名内部类的使用
// btn.addActionListener(new ActionListener() {
// @Override
// public void actionPerformed(ActionEvent e) {
// JOptionPane.showMessageDialog(win,"点击成功");
// }
// });
// 简化匿名内部类
btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "test"));
// 把按钮对象添加到窗口展示
panel.add(btn);
win.setSize(400, 300);
win.setLocationRelativeTo(null);
win.setVisible(true);
}
}
常用API-Object、Objects
Object类的作用:
- 一个类要么默认继承了Object类,要么简介继承了Object类,Object类是Java中的祖宗类。
- Object类的方法是一切子类都可以直接使用的
package com.mochu.d9_object;
public class Student{
private String name;
private String gender;
private int age;
public Student() {
}
public Student(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
package com.mochu.d9_object;
public class Test {
public static void main(String[] args) {
Student stu = new Student("mochu7", "male", 19);
String rs = stu.toString();
System.out.println(rs);
System.out.println(stu.toString());
}
}
// /**
// * 重写equals方法,比较两个对象的内容是否相同
// * this -> s1
// * o -> s2
// * @param o
// * @return
// */
// @Override
// public boolean equals(Object o) {
// // 判断o是否为学生类
// if(o instanceof Student) {
// Student s2 = (Student) o;
if(this.name.equals(s2.name) && this.gender.equals(s2.gender) && this.age == s2.age) {
return true;
}else {
return false;
}
// return this.name.equals(s2.name) && this.gender.equals(s2.gender) && this.age == s2.age;
// }else {
// return false;
// }
// }
/**
* 官方重写equals方法
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name) && Objects.equals(gender, student.gender);
}
package com.mochu.d9_object;
public class Test2 {
public static void main(String[] args) {
Student s1 = new Student("mochu7", "male", 19);
Student s2 = new Student("mochu7", "male", 19);
System.out.println(s1.equals(s2));
}
}
Objects
Objects概述:
- Objects类与Object还是继承关系,Objects类是从JDK1.7开始之后有的。
常用API-StringBuilder
StringBuilder概述:
StringBuilder
是一个可变的字符串类,可以看成一个对象容器。- 作用:提高字符串操作效率,如拼接、修改等。
package com.mochu.d11_api_stringbuilder;
public class Test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("mo");
sb.append("chu");
sb.append(7);
System.out.println(sb);
StringBuilder sb1 = new StringBuilder();
// 支持链式编程
sb1.append("m").append("o").append("c").append("h").append("u").append("7");
System.out.println(sb1);
// 反转
sb1.reverse().append("abc");
System.out.println(sb1);
System.out.println(sb1.length());
// 注意StringBuilder只是手段,最终返回的最好还是字符串类型
StringBuilder sb2 = new StringBuilder();
sb2.append("123").append("abc");
String rs = sb2.toString();
check(rs);
}
public static void check(String s) {
System.out.println(s);
}
}
package com.mochu.d11_api_stringbuilder;
public class StringBuilderTest {
public static void main(String[] args) {
int[] arr0 = null;
System.out.println(toString(arr0));
int[] arr1 = {11, 22, 33, 44, 55};
System.out.println(toString(arr1));
int[] arr3 = {};
System.out.println(toString(arr3));
}
public static String toString(int[] arr) {
if(arr == null) return null;
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(i).append(i == arr.length -1 ? "" : ", ");
}
sb.append("]");
return sb.toString();
}
}
常用API-Math、System、BigDecimal
Math类:包含执行基本数字运算的方法,Math类没有提供公开的构造器。
package com.mochu.d12_math;
public class Test {
public static void main(String[] args) {
// 绝对值
System.out.println(Math.abs(-7));
// 向上取整
System.out.println(Math.ceil(3.14));
// 向下取证
System.out.println(Math.floor(4.9));
// 指数
System.out.println(Math.pow(2, 10));
// 四舍五入
System.out.println(Math.round(4.5));
// 随机数 [0,1)之间
System.out.println(Math.random());
}
}
package com.mochu.d13_system;
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
System.out.println("开始");
// System.exit(0); // JVM终止
// 时间戳 毫秒级
long time = System.currentTimeMillis();
System.out.println(time);
int[] arr1 = {10, 20, 30, 40, 50, 60, 70};
int[] arr2 = new int[6];
System.arraycopy(arr1, 2, arr2, 2, 3);
System.out.println(Arrays.toString(arr2));
}
}
package com.mochu.d14_bigdecimal;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Test {
public static void main(String[] args) {
// 浮点型运算的时候直接加、减、乘、除可能会出现数据失真(精度问题)
System.out.println(0.09 + 0.01);
System.out.println(1.0 - 0.32);
System.out.println(1.015 * 100);
System.out.println(1.301 / 100);
System.out.println("-----------------------");
double a = 0.1;
double b = 0.2;
double c = a + b;
System.out.println(c);
System.out.println("-----------------------");
// 包装浮点型整数成为大数据对象BigDemciaml
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
// BigDecimal c1 = a1.add(b1);
// BigDecimal c1 = a1.subtract(b1);
// BigDecimal c1 = a1.multiply(b1);
BigDecimal c1 = a1.divide(b1);
System.out.println(c1);
// 目的 double
double rs = c1.doubleValue();
System.out.println(rs);
// 注意:BigDecimal一定要精度运算的
BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
BigDecimal c11 = a11.divide(b11, 2, RoundingMode.HALF_UP); // 保留两位小数、舍入模式
System.out.println(c11);
System.out.println("-----------------------");
}
}