Java运行原理
- 程序运行 ( 程序 -> 被编译成计算机能够识别的指令 -> 操作系统 ->计算机硬件)
java程序运行过程
-
.java文件被编译成.class文件(字节码文件)
-
.class文件通过 JVM (java virtual machine,操作系统[mac\windows\linux]上的java虚拟机) 中的类加载系统、字节码执行引擎 -> 运行内存模型 (主要分为以下几个区域)
-
原空间(方法区):存放class\method\final 等变量
-
线程(虚拟机栈)
- 存放线程的空间(main方法等待下一次cpu调度时确定是否使用)
- 程序计数器
tips:
- 运行内存模型通过线程栈按顺序调用原空间的数据进行计算编译
- 简单数据结构变量存储在栈,引用数据结构变量存储在堆上,栈存储一个引用地址指向堆(这点和js是一致的
-
-
jdk打包为.jar文件上传到服务器 (现在可以用包管理工具)
- 本质:zip包-》可以直接修改后缀为.jar
在idea中被编译
- project -> module -> 被认为是sources的文件夹(可以通过project structure或右键调整)-> 编译后out对应文件夹
垃圾回收机制
-
如何定位垃圾
-
引用计数法
- 核心:对该对象的引用计数,如无被引用,则为垃圾(和js一样
- 问题:无法解决循环引用问题\计数器本身也占据空间内存有开销
-
可达性分析
- 核心:从线程栈main方法根去检查,通过运行顺序中不可达对象为垃圾
-
-
垃圾清除算法:新老生带分区清除(具体八股以后再补充
面向对象基础
基本概念:class -> interface \ method
方法
目的:使外界通过方法去修改class中的private属性(外部代码安全地访问实例字段) ; 其他调用目的
格式:修饰符+返回值+方法名+参数+语句
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
-
private方法 : 内部调用private方法,封装对外接口
-
this变量:始终指向当前实例(在参数没有同名的情况下可以不用写this)
-
方法参数:必须严格按照定义的数据类型、顺序
-
可变参数:
类型...可传入类似数组(不限制数量)的类型且保证不是nullpublic void setNames(String... names){ this.names=names; } o.setNames("lili","duud"); o.setNames(); //传入0个参数默认是空数组 -
参数绑定
- 基本类型参数:原值被修改,由于绑定的是之前复制的值,参数不受影响
- 引用类型参数:由于存储的是指针,指向同一个对象,任意一方对这个对象的修改都会影响到彼此(浅拷贝
构造方法
本质:调用new()的函数
tips:Java 类的初始化顺序是:字段初始化 -> 静态初始化块 -> 实例初始化块 -> 构造函数
结构:
class Student{
public Student(){
//与class同名的函数
}
}
注意:
-
重写构造函数之后,原有不带参数的方法失效,如两种都需要,重写时需要都写上
-
可以初始化字段;没有在构造函数初始化字段时,引用类型默认为null,基本类型为默认值(0,false)
-
可以使用多种构造方法,java内部会根据传递的参数自动匹配
-
可以使用
this(...)调用其他构造方法class Student{ //没有初始化字段 private String name;//null private age;//0 //有初始化字段 private String name="unnamed"; private int age=10; public Student(){ //这种方式构造采取初始化字段 } public Student(String name,int age){ this.name=name; this.age=age; } public Student(String name){ this.name=name; this.age=14; } //上一个构造方法用this改写⬇ public Student(String name){ this(name,14) } }
方法重载
本质:方法同名,各自传入参数不同,return type一般相同
继承
子类使用 extends 关键字继承,即可获得父类所用的属性和方法( 注意:子类严禁定义与父类重名的属性字段)
- 继承树 : 类似js原型链,最终指向object->null
修饰符
protected
父类private -> 子类不能访问 ;父类protected -> 子类及孙类等以后都可以访问
class Person{
protected int age;
}
class Male extends Person{
public String sayHi(){
return "hi,you are" + age; //可以访问
}
}
super
子类引用父类字段 -> super.fileName ,楼上那种情况使用super与否影响不大
但是,在任何class的构造函数中会首先调用父类的构造方法super()
图下报错原因:Person没有无参数的构造方法
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super();//这里会默认调用Person的构造方法
//解决办法:
super(name,age);//手动添加参数or给父类添加无参数的构造方法
this.score = score;
}
}
如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。
这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
sealed \ permits
限定只允许什么类继承
public sealed class Person permits Female,Male {
//Person类只允许Female、Male继承
}
转型
向上转型
子类安全地抽象为父类
假设 Person -> Male (Male extends Person)
Person p = new Male() //这样是可以的
向下转型
父类强制转为子类型,如果父类指向子类实例-》成功,否则失败
还是假设 Person -> Male (Male extends Person)
Person p1 = new Male(); //指向子类实例
Person p2 = new Person(); //指向自身
Male m1=(Male)p1; //转型成功
Male m2=(Male)p2;//runtime error! ClassCastException
-
instanceof : 判断实例是不是某种类型,true/false
if(p instanceof Male){ Male m1=(Male)p; //java14后这一步可省略不写,强制转型 System.out.printIn(p.name()); }- instanceof 可以继承extends关系
class Btype {} class Atype extends Btype {} class Type extends Atype {} Type n = new Type(); n instanceof Type //true n instanceof Atype //true n instanceof Btype //true
继承vs组合
继承 -> is 关系 ;组合 -> has关系
Female extends Person{//...继承
protected Clothes dress;//属性组合了一个class为Clothes的dress
}
覆写(override)
子类定义了一个与父类签名完全相同的方法 (名字、参数、返回值都相同)
class Person{
public void run(){
....
}
}
class Male extends Person{
@Override //表示override
public void run(){
...
}
}
- override时候可以通过super调用父类方法
final
extends 允许子类override父类方法
final 不允许override:
-
不允许被继承
final class Person{} -
不允许方法被override
class Person{ public final void sayHi(){ ....//子类不能override该method } } -
不允许实例字段被修改
class Person{ public final String name="lili" } Person p=new Person(); p.name="huhu"; //compile error! -
保证实例被创建后就不可修改( 用在构造方法当中 )
class Person{ public final int age; public Person(int age){ this.age=age; } }
多态
从override引申问题-》如果变量的声明类型不是其实际类型
//Male extends Person
Person p = new Male()
如果Person和Male中都有run方法,p会调用哪一个?-> Male中的
多态含义:Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。
使用:在不修改父类代码的前提下,添加更多类型的子类来实现功能拓展
抽象类
在多态的基础上,如果父类的方法本身不需要实现任何功能,仅仅只是为了让子类去override,可以把父类的方法声明为abstract类:
abstract class Person{
public abstract void run();
}
Person p=new Person(); //compile error!
注意:abstract class 无法被实例化
本质:上层代码定义规范并 abstract(定义接口规范),具体业务逻辑由子类实现
接口
没有实例字段,所有方法都是抽象方法的抽象类 -> 改写为interface
interface也是一种数据类型,可以向上or向下转型
abstract class Person{
public abstract void run();
public abstract String talk();
}
interface Person{
void run();
String talk();
}
abstract class vs interface
相同:
- 二者都可以定义抽象方法(只有抽象方法的抽象类可以转化为interface)
不同:
-
字段:abstract class 可以定义实例字段 ;interface 不可以定义实例字段,但是可以定义public static final字段
-
继承: class只能extends一个class ; interface具体的class可以
implements多个class- interface可以
extends另一个interface
- interface可以
//字段
abstract class Person{
private int age;
}
interface Person{
void run();
}
//extends vs implements
abstract class Male extends abstract Person{...
}
class Male implements Person,Student{
private String name;
@Override
public void run(){...}
@Override
public String talk(){...}
}
//interface extends interface
interface Person {...}
interface Female extends Person{...}
-
使用关系:先使用abstract class生成实例,向上转型为interface使用
-
default方法:一般而言,interface只能写方法名,具体的class再override ,default方法使得在interface中可以使用
default写方法体,子类实例直接使用interface Person{ void run(); default String talk(){ ... } } class Male implements Person{...//这里不必写talk方法} //使用 Person p = new Male(); p.talk()-
可能出现的问题:class implements 多个default 方法名相同的interface
public interface A{ default void print(){ System.out.printIn("A!")} } public interface B{ default void print(){ System.out.printIn("B!")} } public class Test implements A,B {...} Test t = new Test(); t.print() //报错!-
解决方法
// 1 - class创建自己的default方法覆盖接口 public class Test implements A,B{ default void print(){....} } // 2 - super指定使用哪一个接口方法 public class Test implements A,B{ public void print(){ B.super,print(); } }
-
-
tips:
-
interface取决于实际类型
Atype o =new Btype()o指向Btype , 会采取Btype的方法 -
子类可以通过@Override 覆写 default方法 ,以变量实际最近一层@Override为主
interface M { default void tell(){System.outprintIn('M')} } interface A extends M { @Override public void tell(){ System.outprintIn('A') } } interface B extends A { @Override public void tell(){ System.outprintIn('B') } } //... A test = new B(); //实际指向B test.tell() //B
-
-
static
核心:属于class,不属于instance
-
静态字段属于class本身不属于每一个实例,每一个实例都可以修改且直接影响到class本身(类似浅拷贝),一般使用class.staticName而不推荐使用instance.staticName去修改
-
静态方法可以通过class.直接去调用,类似函数,常用于工具函数的编写,静态字段和静态
-
interface中的字段都是static字段,且都是
public static final的字段,public interface Person{ public static final int AGE=18; //可以简写,编译器会自动加上publi static final int AGE=18; }
包package
- 包名要按照反域名规定,不要与java.lang的类、jdk常用class重名,不同包下即使同名的class也属于不同class
- 包作用域:同一个包的class,可以访问包作用域的字段和方法(不用public private protected修饰的)
- import 和前端理解一致
作用域
- public : class可以被别的访问,method\field需要先能够访问到这个class才能访问
- private : 限定于本class内部,无法被其他class访问,(嵌套类除外->属于本class内部)声明时习惯声明到public之后
- protected :被extends的class访问
- package : 允许访问本包内没有public\private 的 class , 没有public \ private \ protected 修饰的field \method
- finnal : 不允许继承、override、重新赋值
- 局部变量:变量声明处->对应块{}结束
最佳实践:尽可能少使用public ,使用package进行测试,.java文件只能包含一个public class,但可以包含多个非public class
内部类
-
Inner class : 一个类定义在另一个类的内部
- 实例化时inner class必须依附于outer class
- Inner可以修改Outer的
private字段
class Outer{ class Inner{...} } //new Outer o = new Outer(); 0 Inner i = o.new Inner(); -
Anonymous Class : 匿名类,在class内部通过方法实例化一个类
- 可以修改Outer的
private字段
- 可以修改Outer的
-
Static Nested Class : 静态内部类
- 独立的类
- 可以访问Outer的
private字段