1.面向对象
对象内存分析
举例:
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 = "郭同学";
}
}
内存分析图:
类的成员
类成员之一:成员变量
成员变量声明
成员变量 vs 局部变量
类成员之二:方法
方法调用内存分析
举例:
public class Person {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
}
public static void eat() {
sleep();
System.out.println("人:吃饭");
}
public static void sleep(){
System.out.println("人:睡觉");
doSport();
}
public static void doSport(){
System.out.println("人:运动");
}
}
方法重载 (overload)
最经典的方法重载案例
可变形参
方法递归
递归方法调用:方法自己调用自己的现象就称为递归。
- 递归方法包含了一种隐式的循环。
- 递归方法会重复执行某段代码,但这种重复执行无须循环控制。
- 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,停不下来,类似于死循 环。最终发生栈内存溢出。
递归的分类: 直接递归、间接递归。
注意
类成员之三:构造器
语法格式
特点
- 如果没有显式手动声明类的构造器时,系统会默认提供一个无参构造器,且该构造器的修饰符与默认与类的修饰符相同
- 如果显式定义类的构造器后,系统就不会再提供默认的无参构造器了
- 在类中,至少会存在一个构造器
- 构造器是可以重载的
this()
同一个类中构造器互相调用
注意点
类成员之四:代码块
静态代码块
特点
public class Chinese {
// private static String country = "中国";
private static String country;
private String name;
{
System.out.println("非静态代码块,country = " + country);
}
static {
country = "中国";
System.out.println("静态代码块");
}
public Chinese(String name) {
this.name = name;
}
}
public class TestStaticBlock {
public static void main(String[] args) {
Chinese c1 = new Chinese("张三");
Chinese c2 = new Chinese("李四");
}
}
非静态代码块
特点
面向对象特征
面向对象特征之一:封装性
"高内聚、低耦合"
什么是封装性?
通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思 想。
面向对象特征之二:继承
继承性
方法重写 (override/overwrite)
方法重写的要求
跨包必须pulbic
super
调用父类被重写的方法
调用父类中同名的成员变量
子类构造器调用父类构造器
小结:this与super
this 和 super的使用格式
面向对象特征之三:多态
一千个读者眼中有一千个哈姆雷特。
多态的理解
运行时、编译时
多态的好处/弊端
开闭原则 OCP
虚方法调用
成员变量没有多态性
向上/下转型
为什么要类型转换?
如何向下/上转型?
instanceof关键字
Object根父类
Object类的方法
根据 JDK 源代码及 Object 类的 API 文档,Object 类当中包含的方法有 11 个。
这里我们主要关注其中的 6 个:
(重点)equals()
(重要)toString()
clone()
public static void main(String[] args) {
Animal a1 = new Animal("花花");
try {
Animal a2 = (Animal) a1.clone();
System.out.println("原始对象:" + a1);
a2.setName("毛毛");
System.out.println("clone 之后的对象:" + a2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
class Animal implements Cloneable{
private String name;
public Animal() {
super();
}
public Animal(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal [name=" + name + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
finalize()
在 JDK 9 中此方法已经被标记为过时的。
public class FinalizeTest {
public static void main(String[] args) {
Person p = new Person("Peter", 12);
System.out.println(p);
p = null;//此时对象实体就是垃圾对象,等待被回收。但时间不确定。
System.gc();//强制性释放空间
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
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;
}
//子类重写此方法,可在释放对象前进行某些操作
@Override
protected void finalize() throws Throwable {
System.out.println("对象被释放--->" + this);
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
getClass()
hashCode()
native关键字
static关键字
如果想让一个成员变量被类的所有实例所共享,就用 static 修饰即可,称为类 变量(或类属性)!
类属性、类方法的设计思想
静态变量
静态方法
单例(Singleton)设计模式
设计模式
何为单例模式?
饿汉式
无论用不用得到,总会加载一个实例,就像吃货的身上总会带着食物
饿汉式是一种在类加载时就创建实例的单例模式。它的特点是无论是否会被使用到,实例对象都在类加载时被创建。
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
懒汉式
不需要的时候懒得加载实例,用到的时候才加载实例,默认为null
- 懒汉式属于一种延迟加载的单例模式,它的特点是在第一次使用时创建实例对象,而不是在类加载时就创建。
- 懒汉式可以避免在不需要实例对象时的资源浪费,只有在需要时才进行创建。这种延迟加载的特性使得它在某些情况下更加高效。
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if(single == null) {
single = new Singleton();
}
return single;
}
}
懒汉式 VS 饿汉式
单例模式的优点及应用场景
main方法如何理解?
final关键字
final:最终的,不可更改的
final修饰类 (太监类)
final修饰方法
final修饰变量
抽象类与抽象方法 (abstract关键字)
理解抽象类
语法格式
使用说明
注意事项
抽象类中可以定义构造方法吗?
模板方法设计模式
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);
}
}
}
接口
声明格式
接口成员
- Java8之前,接口只能写 公共的抽象方法 和 公共的静态常量
- Java8开始,接口引入了 默认方法 和 静态方法 的概念,这两种方法都可以有方法体,不需要实现类去实现,默认方法可以被实现类重写,静态方法则不能被实现类直接使用,只能被接口自身调用
这两种方法的引入解决了接口升级和维护的问题,使得接口可以更加灵活地扩展功能
- Java9开始,接口还可以定义私有方法,这些私有方法只能被接口内部的其他方法调用,本能被接口的实现类直接访问和调用
使用规则
类实现接口
接口的多实现
接口的多继承
- 一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子 接口继承父接口的方法。
- 所有父接口的抽象方法都有重写。
- 方法签名相同的抽象方法只需要实现一次。
public interface Chargeable {
void charge();
void in();
void out();
}
定义子接口:
public interface UsbC extends Chargeable,USB3 {
void reverse();
}
定义子接口的实现类:
public class TypeCConverter implements UsbC {
@Override
public void reverse() {
System.out.println("正反面都支持");
}
@Override
public void charge() {
System.out.println("可充电");
}
@Override
public void in() {
System.out.println("接收数据");
}
@Override
public void out() {
System.out.println("输出数据");
}
}
接口与实现类对象构成多态引用
使用接口的静态成员
接口不能直接创建对象,但是可以通过接口名直接调用接口的静态方法和静态 常量。
使用接口的非静态方法
JDK8中相关冲突问题
默认方法冲突原则
(1)类优先原则
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口 中的抽象方法重名,子类就近选择执行父类的成员方法。
(2)接口冲突(左右为难)
- 当一个类同时实现了多个父接口,而多个父接口中包含方法签名相同的默认方法时, 怎么办呢?无论你多难抉择,最终都是要做出选择的。
解决办法:子类通过接口名.super.方法名()区分
-----------------------------------------------------------------------------------------------------------------------
- 当一个子接口同时继承了多个接口,而多个父接口中包含方法签名相同的默认方法 时,怎么办呢?
解决办法:子接口通过重写默认方法区分
常量冲突问题
接口的总结
abstract与interface
不同点
- 核心区别
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法,子类必须重写所有的抽象方法。
相同点
// 抽象类
abstract class Animal {
abstract void makeSound();
}
// 实现抽象类
class Dog extends Animal {
void makeSound() {
System.out.println("Woof!");
}
}
// 接口
interface Flyable {
void fly();
}
// 实现接口
class Bird implements Flyable {
void fly() {
System.out.println("I'm flying!");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound(); // 输出: Woof!
Bird bird = new Bird();
bird.fly(); // 输出: I'm flying!
}
}
内部类
分类
成员内部类
使用规则
创建成员内部类对象
public class TestMemberInnerClass {
public static void main(String[] args) {
//创建静态内部类实例,并调用方法
Outer.StaticInner inner = new Outer.StaticInner();
inner.inFun();
//调用静态内部类静态方法
Outer.StaticInner.inMethod();
System.out.println("*****************************");
//创建非静态内部类实例(方式 1),并调用方法
Outer outer = new Outer();
Outer.NoStaticInner inner1 = outer.new NoStaticInner();
inner1.inFun();
//创建非静态内部类实例(方式 2)
Outer.NoStaticInner inner2 = outer.getNoStaticInner();
inner1.inFun();
}
}
class Outer{
private static String a = "外部类的静态 a";
private static String b = "外部类的静态 b";
private String c = "外部类对象的非静态 c";
private String d = "外部类对象的非静态 d";
static class StaticInner{
private static String a ="静态内部类的静态 a";
private String c = "静态内部类对象的非静态 c";
public static void inMethod(){
System.out.println("Inner.a = " + a);
System.out.println("Outer.a = " + Outer.a);
System.out.println("b = " + b);
}
public void inFun(){
System.out.println("Inner.inFun");
System.out.println("Outer.a = " + Outer.a);
System.out.println("Inner.a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
// System.out.println("d = " + d);//不能访问外部类的非静态成
员
}
}
class NoStaticInner{
private String a = "非静态内部类对象的非静态 a";
private String c = "非静态内部类对象的非静态 c";
public void inFun(){
System.out.println("NoStaticInner.inFun");
System.out.println("Outer.a = " + Outer.a);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("Outer.c = " + Outer.this.c);
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
public NoStaticInner getNoStaticInner(){
return new NoStaticInner();
}
}
局部内部类
非匿名局部内部类
public class TestLocalInner {
public static void main(String[] args) {
Outer.outMethod();
System.out.println("-------------------");
Outer out = new Outer();
out.outTest();
System.out.println("-------------------");
Runner runner = Outer.getRunner();
runner.run();
}
}
class Outer{
public static void outMethod(){
System.out.println("Outer.outMethod");
final String c = "局部变量 c";
class Inner{
public void inMethod(){
System.out.println("Inner.inMethod");
System.out.println(c);
}
}
Inner in = new Inner();
in.inMethod();
}
public void outTest(){
class Inner{
public void inMethod1(){
System.out.println("Inner.inMethod1");
}
}
Inner in = new Inner();
in.inMethod1();
}
public static Runner getRunner(){
class LocalRunner implements Runner{
@Override
public void run() {
System.out.println("LocalRunner.run");
}
}
return new LocalRunner();
}
}
interface Runner{
void run();
}
匿名内部类
枚举类
开发中,当需要定义一组常量时,强烈建议使用枚举类。
定义枚举类
JDK5之前
JDK5之后
ENUM定义的要求和特点
ENUM中常用方法
import java.util.Scanner;
public class TestEnumMethod {
public static void main(String[] args) {
//values()
Week[] values = Week.values();
for (int i = 0; i < values.length; i++) {
//ordinal()、name()
System.out.println((values[i].ordinal()+1) + "->" + values
[i].name());
}
System.out.println("------------------------");
Scanner input = new Scanner(System.in);
System.out.print("请输入星期值:");
int weekValue = input.nextInt();
Week week = values[weekValue-1];
//toString()
System.out.println(week);
System.out.print("请输入星期名:");
String weekName = input.next();
//valueOf()
week = Week.valueOf(weekName);
System.out.println(week);
input.close();
}
}
实现接口的枚举类
语法
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;
}
}
注解
注解与注释
注解的重要性
常见的注解作用
三个最基本的注解
import java.util.ArrayList;
public class TestAnnotation {
@SuppressWarnings("all")
public static void main(String[] args) {
int i;
ArrayList list = new ArrayList();
list.add("hello");
list.add(123);
list.add("world");
Father f = new Son();
f.show();
f.methodOl();
}
}
class Father{
@Deprecated
void show() {
System.out.println("Father.show");
}
void methodOl() {
System.out.println("Father Method");
}
}
class Son extends Father{
/* @Override
void method01() {
System.out.println("Son Method");
}*/
}
元注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABL
E})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, P
ARAMETER, TYPE})
public @interface Deprecated {
}
自定义注解(未完)
Java 实现自定义注解_java自定义注解-CSDN博客
JUnit单元测试(未完)
测试分类
JUnit单元测试介绍
JUnit使用
JUnit4.x版本要求
创建Maven JavaSE项目
导入依赖
junit-4.12.jar
//junit-4.11之后需要自己导入该包
hamcrest-core-1.3.jar
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest-core -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
包装类
包装类与基本数据类型的转换
装箱
拆箱
自动装箱与自动拆箱
基本数据类型、包装类与字符串的转换
(1)基本数据类型转字符串
(2)字符串转基本数据类型
其他方式小结
包装类的其他API
数据类型的最值
字符转大小写
整数转进制
比较的方法
包装类对象特点
包装类缓存对象
Double和Float不缓存对象,存的地址,只比较地址
类型转换问题
包装类对象不可变
public class TestExam {
public static void main(String[] args) {
int i = 1;
Integer j = new Integer(2);
Circle c = new Circle();
change(i,j,c);
System.out.println("i = " + i);//1
System.out.println("j = " + j);//2
System.out.println("c.radius = " + c.radius);//10.0
}
/*
* 方法的参数传递机制:
* (1)基本数据类型:形参的修改完全不影响实参
* (2)引用数据类型:通过形参修改对象的属性值,会影响实参的属性值
* 这类 Integer 等包装类对象是“不可变”对象,即一旦修改,就是新对象,和
实参就无关了
*/
public static void change(int a ,Integer b,Circle c ){
a += 10;
// b += 10;//等价于 b = new Integer(b+10);
c.radius += 10;
/*c = new Circle();
c.radius+=10;*/
}
}
class Circle{
double radius;
}
Java17版本的包装类
不知道哪个版本开始,这个被弃用且移除了
下一章: