java运行机制
JDK:Java Development Kit (开发者工具包)
JDK ⊃ JRE ⊃ JVM
内存和内存地址
内存:软件在运行过程中,临时存储的数据
内存地址:内存会以字节(1byte)为单元,划分成很多区域,每个区域对应一个编号
地址范围:(会转化为16进制,方便阅读)
基本数据类型:栈内存直接存值
应用数据类型:栈内存中存储值的索引
数组
1. 存储为连续的空间
2. 一旦定义,长度不变
静态初始化数组
遍历数组
int[] ageArr1 = new int[]{1, 2, 3, 4, 5, 6};
for (int i = 0; i < ageArr1.length; i++) {
System.out.println(ageArr1[i]);
}
for (int j : ageArr1) {
System.out.println(j);
}
动态初始化
默认初始化的值
方法
// 代码结构
public static 返回值类型 方法明(参数1, 参数2) {
方法逻辑
return 返回值;
}
// 示例
public static int sum(int a, int b) {
int result = a + b;
return result;
}
方法的重载
1. 方法名相同
2. 但各自的(形参)参数不同【个数不同,类型不同,顺序不同,都算】
称为方法重载(`Overload`)。
public static void myHello() {
System.out.println("你好");
}
public static void myHello(String name) {
System.out.println("你好,我是" + name);
}
public static void myHello(String name, int age) {
System.out.println("你好,我是" + name + "我今年" + age);
}
不构成方法的重载,重载只看方法名和参数
public static void myHello() {
System.out.println("你好");
}
public static String myHello() {
return "你好";
}
面向对象(一种编程思想)
class和instance class类,就相当于模具, instance实例,就相当于模具中生产的东西
测试类和javabean类
javabean类 (描述一类事物)
package classStuden;
public class People {
// 属性
String name;
int age;
// 行为,方法
public void say() {
System.out.println("我是人类");
}
}
测试类(带有main方法,是程序的主入口)
package classStuden;
public class Test {
public static void main(String[] args) {
People p1 = new People();
p1.say();
}
}
工具类 (帮我们做事情的类,如求最大值啊之类的)
public class ArrayUtil {
// 私有化构造方法,目的:不让外界创建构造函数
private ArrayUtil() {
}
//获取数组的平均值
public static void getAverage(int[] arr) {
int sum = 0;
int length = arr.length;
for (int j : arr) {
sum += j;
}
System.out.println("该数组的平均值是:" + sum * 1.0 / length);
}
}
访问控制修饰符【权限修饰符】(4 个)
public String name;
protected String sexy;
int age; // 没有修饰符,默认就是default
private int score;
不控制访问权限,而是定义特性、状态、行为,比如静态、不可变、抽象等。
static 关键字
不属于对象,属于类
在class里,定义共享数据,无论哪个实例化对象修改了这个值,所有实例化对象的值都会被修改**(所有对象共享)**
静态变量是随着类加载而加载的,优先于对象出现的(也就是说,类还没有实例化之前,静态变量就在堆内存中开辟空间了)
public class Student {
static String teacher;
String name; // 成员变量
int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void getTeacherName() {
System.out.println("老师的名字是:" + Student.teacher);
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("小明", 18);
Student s2 = new Student("小红", 20);
Student.teacher = "张雪峰";
s1.getTeacherName(); // 输出:张雪峰
s2.getTeacherName(); // 输出:张雪峰
}
}
static 修饰静态方法
静态方法多用在测试类和工具类中,javabean类很少使用
- 静态方法只能访问静态变量和其他静态方法
- 非静态方法,可以访问其他静态变量和其他静态方法,也可以访问非静态的变量和方法
- 静态方法里面不能用this关键字,要用 类名.静态变量(静态方法)
public class StaticUtil {
String name; // 大名
static String xiaoName; // 小名
int age; // 岁数
int virtualAge; // 虚岁
public void getName() {
// 可以访问所有变量和方法
}
public static void getXiaoName() {
// 只能访问静态变量(xiaoName, virtualAge)和静态方法(getVirtualAge)
}
public void getAge() {
}
public static void getVirtualAge() {
}
}
public和static的区别是什么
public 是权限修饰符,控制在别的包里面,能不能访问你
static 是指将方法或者变量归结到类上,随着类的加载而加载,可以直接使用 类名.方法名 的形式调用,不需要new
public 就像 js 里面的 export
public static 组合使用就约等于 export function
final 关键字(不可变 / 不可修改 / 不能被覆盖)相当于JS里面的 const
this关键字
用于区分成员变量和局部变量的
public class Student {
String name; // 成员变量
public void setName(String name) { // 局部变量(参数)
// 这里 this.name = 成员变量
// 这里 name = 传入的参数
this.name = name;
}
}
本质:this指的就是所在方法调用者的地址值,谁调用就指向谁
public class Student {
String name; // 成员变量
int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void getName() {
String name = "局部名称";
// 就近原则
System.out.println("局部变量的名称" + name);
// 获取成员变量
System.out.println("成员变量的名称" + this.name);
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("小明", 18);
Student s2 = new Student("小红", 20);
s1.getName(); // 此时this指向s1这个地址
s2.getName(); // 指向s2的地址
}
}
构造方法,构造函数
作用:在创建(实例化)对象的时候,给成员变量赋值
构造方法的特点:
- 必须和类名一样,且不写的时候,会默认帮你创建一个无参数的构造方法
- 如果写了任意构造方法,系统不再提供无参数的构造方法
- 构造方法也可以重载
- 没有返回值类型,也没有void
- 没有具体返回值,也不能写return
构造方法的执行时机:
- 创建对象时,由虚拟机调用,不能手动调用
- 每创建一次对象,就会调用一次构造方法
public class People {
// 属性
public String name;
protected String sexy;
int age;
private int score;
public People() {
}
public People(String name, String sexy, int age, int score) {
this.name = name;
this.sexy = sexy;
this.age = age;
this.score = score;
}
}
People p1 = new People("cmh","男", 18, 100);
枚举(枚举类,特殊的类)
可创建的对象个数是有限的
默认继承
Enum类每一个枚举项(SPRING、SUMMER)都是 该类的 public static final 实例对象
枚举类第一行必须是枚举项,以逗号分割,分号结束
枚举类的构造方法,必须是private,目的是不然外界创建超过数量的对象
public enum 枚举类名 {
// 属性
// 方法
}
public enum SeasonEnum {
// 这里其实都是相当于使用 new 实例化出来的对象,调用的就是下面的 SeasonEnum 构造方法,
// 所以在使用的时候,直接使用【类名.枚举值】的形式就可以了,不需要再new了
// 且这里的枚举值,默认都是使用 【 public static final 】 修饰的
SPRING(1, "春天"),
SUMMER(2, "夏天"),
AUTUMN(3, "秋天"),
WINTER(4, "冬天");
// 枚举可以有成员变量
private final int code;
private final String name;
// 构造方法(默认就是 private,且必须是private,不能 public)
SeasonEnum(int code, String name) {
this.code = code;
this.name = name;
System.out.println("构造函数被执行了:" + this.name); // 会发现直接输出四个
}
// 可以写普通方法
public String getInfo() {
return code + ":" + name;
}
}
面向对象的三大特征(封装、继承、多态)
继承 extends
什么是继承
父类是通用模板,子类是在模板基础上定制化的版本。
继承的目的
- 代码复用
- 扩展功能
- 多态的基础,没有继承就没有多态
继承的重要规则
- Java 只支持单继承,一个子类只能继承一个父类,但是可以多层继承,就像原型链一样,继承的顶点是object
- 子类不能继承父类的私有成员,
private修饰的属性和方法,子类无法直接访问- 构造方法不能被继承,只能通过 super() 调用
- 子类可以重写(override)父类方法
什么是重写
子类把父类的方法重新写一遍,方法名、参数列表、返回值都相同,只有方法体不同。
在重写的方法体中,可以使用 super.方法 的形式,调用父类的方法,正在被重写的这个方法也能被调用
super 关键字(继承必备)
super代表父类对象,用于子类中访问父类的成员。
- 调用父类构造方法:
super();- 调用父类属性:
super.属性名- 调用父类被重写的方法:
super.方法名()
public class Student extends People {
// 可以把多个子类中的方法,提取到父类中去,提高代码的复用性
// 子类可以在父类的基础上,增加其他功能,使子类更强大
}
多态(事物的多种形态)
什么是多态
同一个行为,在不同对象上表现出不同的形态 / 结果
父类引用指向子类对象,调用重写的方法时,会执行子类的实现,而不是父类的。
一句话总结:编译看左边,运行看右边
多态的前提条件(必须同时满足)
- 继承 / 实现:必须有子类继承父类,或实现类实现接口
- 方法重写:子类必须重写父类的方法(多态的核心)
- 父类引用指向子类对象:
父类 引用名 = new 子类();
public class Animal {
// 父类方法
public void shout() {
System.out.println("动物叫");
}
}
public class Dog extends Animal {
// 重写父类方法
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
public class Cat extends Animal {
// 重写父类方法
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
public class Test {
public static void main(String[] args) {
// 多态:父类引用 指向 子类对象
Animal animal1 = new Dog();
Animal animal2 = new Cat();
// 同一个方法 shout(),执行结果不同!
animal1.shout(); // 输出:汪汪汪
animal2.shout(); // 输出:喵喵喵
}
}
多态现象
animal1和animal2都是Animal类型,但调用同一个shout()方法,执行的是各自子类的代码。这就是多态。
多态的作用(为什么要用它?)
- 提高代码的可扩展性 (最核心作用)
- 减少重复代码,简化程序
- 解耦:降低代码耦合度
- 统一程序设计标准
多态中成员变量 / 静态方法的特点(面试常考)
成员变量:没有多态!永远看左边(父类)
静态方法:没有多态!永远看左边(父类)
只有【实例方法】存在多态
class Animal {
String name = "动物";
public static void sleep(){
System.out.println("动物睡");
}
}
class Dog extends Animal {
String name = "小狗";
public static void sleep(){
System.out.println("小狗睡");
}
}
// 测试
Animal a = new Dog();
System.out.println(a.name); // 动物(变量无多态)
a.sleep(); // 动物睡(【静态方法】无多态)
多态的向上转型与向下转型
- 向上转型(自动发生)
父类 引用 = new 子类();
就是我们上面写的多态,安全、自动。
- 向下转型(强制转换)
目的:把 “伪装成父类” 的子类对象,变回子类本身,从而调用子类独有的方法和属性。
必须配合instanceof判断,否则会报类型转换异常。
抽象类(abstract)
抽象类就是:被设计成 “专门用来被继承、不能直接 new” 的半完成类。
它的作用就是强制子类必须重写抽象方法,不带abstract关键字的方法可以不用重写。
1. 关键字:abstract
- 抽象类:
abstract class - 抽象方法:
abstract void 方法名();没有方法体
// 抽象类
public abstract class User {
String name;
// 抽象方法:只有声明,没有实现
public abstract void work(参数);
}
抽象类不能被实例化
new User(); // 报错抽象类里可以有普通方法、属性、构造方法
有抽象方法的类,必须是抽象类(抽象类中可以没有抽象方法,但是有抽象方法的必须是抽象类)
子类继承抽象类,必须重写所有抽象方法,否则报错(强制规范,保证子类一定有某个方法。)
抽象类到底用来干嘛?(核心作用)
- 强制子类必须重写方法
- 把通用代码抽到父类,子类只写自己的逻辑
- 配合多态使用
接口(Interface)
接口就是一套纯规范、纯规则,只定义 “要做什么”,不写 “怎么做”。
接口只规定必须有哪些方法,具体逻辑让实现类自己写。
特点:
- 接口不能被实例化,也就是new
- 接口的子类叫实现类,一个实现类可以有多个接口,叫多实现,也可以在继承一个类的同时实现多个接口
- 实现类要么重写接口的所有方法,要么实现类本身就是一个抽象类
// 先 extends 继承,再 implements 实现多个接口
public class Student extends Person implements Study, Work {
// 实现接口1的方法
@Override
public void study() {
System.out.println("学生在学习");
}
// 实现接口2的方法
@Override
public void work() {
System.out.println("学生的工作是学习");
}
}
接口的成员特点:
- 成员变量只能是产量,默认 public static final 修饰
- 成员方法主要是抽象方法,默认被 public abstract 修饰(JDK8和JDK9之后有扩展)
- 接口没有构造方法
// 接口
public interface Animal {
// 方法默认就是 public abstract
void shout();
}
// 实现类
public class Dog implements Animal {
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
接口和接口之间可以继承,也可以多继承
// 接口1
interface A {
void testA();
}
// 接口2
interface B {
void testB();
}
// 接口3
interface C {
void testC();
}
// D 接口同时继承 A、B、C 三个接口
interface D extends A, B, C {
void testD();
}
JDK8和JDK9之后的扩展
内部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类(最重要,最常用)
成员内部类
public class Outer {
private int a = 10;
// 成员内部类
class Inner {
public void show() {
// 直接访问外部类私有变量
System.out.println(a);
}
}
}
创建步骤
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();
静态内部类(static 修饰)
public class Outer {
private static int b = 20;
// 静态内部类
static class StaticInner {
public void show() {
System.out.println(b);
}
}
}
创建步骤
Outer.StaticInner inner = new Outer.StaticInner();
局部内部类(方法里的类)
public class Outer {
public void method() {
int num = 100;
// 局部内部类
class LocalInner {
public void show() {
System.out.println(num);
}
}
// 只能在方法内创建对象
LocalInner inner = new LocalInner();
inner.show();
}
}
匿名内部类(最重要!开发大量用)
作用:在继承一个类或者实现一个接口的时候少写一个文件,用来快速创建一个 “一次性使用” 的实现类 / 子类对象,也是lambda的前置知识点
父类或接口 对象名 = new 父类或接口() {
// 必须重写所有抽象方法
@Override
public void 方法名() {
// 方法体
}
};
-
没有类名,所以叫 “匿名”
-
只能创建一个对象,用完就丢
-
可以直接访问外部类的所有成员(包括 private)
-
可以访问方法里的变量,但变量必须是 final 或 有效 final
-
本质就是:快速创建子类 / 实现类对象
匿名内部类示例:
先定义一个接口:
interface Study {
void study();
}
不用匿名内部类(传统写法,有名字的实现类)
class Student implements Study {
@Override
public void study() {
System.out.println("学生在学习");
}
}
// 使用
Study s = new Student();
s.study();
用匿名内部类(一步搞定)
Study s = new Study() {
@Override
public void study() {
System.out.println("学生在学习");
}
};
s.study();
为什么这里可以使用接口实例化,其实不是实例化,
【 这里其实是在创建一个 “匿名的、实现了 Study 接口的子类对象”,并用这个匿名的实现类进行实例化的 】
为什么可以直接用Study s来接收实例化?
【 这其实是多态的体现 --> 父 父 = new 子() / User user = new Student() 】
new 没有名字的 java 类 + 继承/实现 + 重写方法 + 创建对象