gtb-3 java运行原理及面向对象基础

62 阅读10分钟

Java运行原理

  • 程序运行 ( 程序 -> 被编译成计算机能够识别的指令 -> 操作系统 ->计算机硬件)

java程序运行过程

  • .java文件被编译成.class文件(字节码文件)

  • .class文件通过 JVM (java virtual machine,操作系统[mac\windows\linux]上的java虚拟机) 中的类加载系统、字节码执行引擎 -> 运行内存模型 (主要分为以下几个区域)

    • 原空间(方法区):存放class\method\final 等变量

    • 线程(虚拟机栈)

      • 存放线程的空间(main方法等待下一次cpu调度时确定是否使用)
      • 程序计数器

      tips:

      1. 运行内存模型通过线程栈按顺序调用原空间的数据进行计算编译
      2. 简单数据结构变量存储在,引用数据结构变量存储在上,栈存储一个引用地址指向堆(这点和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)

  • 方法参数:必须严格按照定义的数据类型、顺序

  • 可变参数:类型...可传入类似数组(不限制数量)的类型且保证不是null

    public void setNames(String... names){
        this.names=names;
    }
    ​
    o.setNames("lili","duud");
    o.setNames(); //传入0个参数默认是空数组
    
  • 参数绑定

    • 基本类型参数:原值被修改,由于绑定的是之前复制的值,参数不受影响
    • 引用类型参数:由于存储的是指针,指向同一个对象,任意一方对这个对象的修改都会影响到彼此(浅拷贝

构造方法

本质:调用new()的函数

tips:Java 类的初始化顺序是:字段初始化 -> 静态初始化块 -> 实例初始化块 -> 构造函数

结构:

class Student{
    public Student(){
        //与class同名的函数
    }
}

注意:

  1. 重写构造函数之后,原有不带参数的方法失效,如两种都需要,重写时需要都写上

  2. 可以初始化字段;没有在构造函数初始化字段时,引用类型默认为null,基本类型为默认值(0,false)

  3. 可以使用多种构造方法,java内部会根据传递的参数自动匹配

  4. 可以使用 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
//字段
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:

      1. interface取决于实际类型 Atype o =new Btype() o指向Btype , 会采取Btype的方法

      2. 子类可以通过@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 字段
  • Static Nested Class : 静态内部类

    • 独立的类
    • 可以访问Outer的 private 字段