Java基础知识

41 阅读9分钟

java的三大分支

  • JavaSE

    JavaSE(Java Platform,Standard Edition) :Java平台标准版。它是 JavaEE 和 JavaME 的基础。

    用来开发C/S架构软件

    JavaSE 包含 Java 语言基础、JDBC(Java数据库连接性)操作、I/O(输出输出)操作、网络通信、多线程等技术它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为 Java Platform,Enterprise Edition(Java EE)提供基础。

  • JavaEE

    Java EE(Java Platform,Enterprise Edition):Java平台企业版。

    主要针对企业应用的开发,例如,电子商务网站、ERP系统,也包括 Web 开发等方面。

    Java EE 是在 JavaSE 的基础上构建的提供 Web 服务、组建模型、管理和通信API(Application Programming Interface,应用程序接口),可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web 应用程序。

  • JavaME

    Java ME(Java Platform,Micro Edition): Java平台微型版。

    它是一套运行专门为嵌入式设备设计的API接口规范。

    Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。

    Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的联网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能。

总结:

​ ①JavaSE:开发电脑上运行的软件(针对桌面程序的开发)。

​ ②JavaEE:开发网站(针对企业级应用的开发)。

​ ③JavaME:开发手机软件(APP,针对嵌入式设备软件的开发)。

java编译方式

java是面向对象的高级语言,高级语言的运行方式:

  • 编程:java程序员写的.java源代码
  • 编译:把.java文件转化成机器认识的格式
  • 运行:让机器执行编译后的指令

java的编译运行方式是混合型的,即根据**.java文件编译出一个.class二进制字节码文件**,通过这个文件到JVM中中进行翻译成二进制让机器处理。

  • java不是直接运行在系统中的,而是运行在jvm虚拟机中的。

  • java语言的跨平台是通过不同的JVM虚拟机实现的。

image-20240508221055172

JDK和JRE

image-20240508221009574

JRE和JDK的区别是,JDK是给开发人员使用的,里面包含了开发工具。JRE是给用户使用的,里面包含了运行工具。

Java内存分配

其中在JVM虚拟机中

jvm的内存分为五个部分:

这个只是在JDK8之前的JVM内存图

在JDK8开始:取消了方法区,增加了元空间,把原来的方法区的多种功能进行拆分,有的功能放在了堆空间中,有的放在了元空间

  • 栈:方法运行时使用的内存,比如main方法运行,进入方法栈中执行

    方法开始执行会进栈,执行完毕会出栈(栈是一种数据结构,具体去学数据结构)

  • 堆:存储对象或数组,new创建的东西都存在堆内存中

    new出来的东西都会在这个内存中开辟空间并且产生地址值

  • 方法区:存储可以运行的class文件(jdk8开始将方法区拆解了,新增元空间,并且元空间存储可以运行的class文件

  • 本地方法栈:JVM在使用操作系统功能的时候使用

  • 寄存器:给CPU使用

数组的内存图

举例:

public static void main(String[] args){
    int[] arr=new int[2];
    sout(arr);
    sout(arr[0]);
    sout(arr[1]);
    
    arr[0]=11;
    arr[1]=22;
    sout(arr[0]);
    sout(arr[1]);
    
    int[] arr2={33,44,55};
    sout(arr2);
    sout(arr1[0]);
    sout(arr2[1]);
    sout(arr2[2]);
}

上述代码执行流程:

  1. main方法压入栈内存中

  2. 执行int[] arr=new int[2];

    具体执行:

    ​ 在堆内存中创建数组空间并产生地址值

    ​ 把这个地址值赋值给arr

  3. 执行代码sout(arr)此时是打印出了arr的地址值

  4. 执行代码sout(arr[0])通过arr找到数组在堆内存中的地址,通过[0]找到对应地址中对应的数据

  5. sout(arr[1])同理

  6. arr[0]=11;如果arr[0]找到堆内存中开辟的空间中的对应数值存放地点,并且给他赋值11

  7. arr[1]=22同理

  8. 执行代码int[] arr2={33,44,55};虽然没有new但是实质还是在堆空间中创建内存

    这俩个内存之间互不干扰

  9. 具体如下

注:如果是这种情况

public static void main(String[] args){
    int[] arr=new int[2];
    int[] arr2=arr;
    //这个代表的是把arr的地址值赋值给arr2
    //此时修改arr的内容同时arr2也会改变!因为arr2存储的只是地址值!!!
}

方法的内存图

方法调用的内存原理

public class Test{
	public static void main(String[] args){
        eat();
	}
    public static void eat(){
        study();
        sout("吃饭");
        sleep();
    }
    public static void sleep(){
        sout("睡觉");
    }
    public static void study(){
        sout("学习");
	}
}

方法被调用后就会进栈运行

当方法执行完毕后,方法会出栈,此时栈内开辟的变量也随着销毁

上面代码执行后如图

当所有方法进栈之后,就会开始执行了,执行完毕后会一个个出栈

对象内存图

  • 一个对象的内存图

    Student s=new Student();

    1. 加载class文件
    2. 声明局部变量
    3. 在堆内存中开辟一个空间
    4. 默认初始化or显示初始化(如果在类中给变量赋值了就加载显示初始化,否则加载默认初始化)
    5. 构造方法初始化
    6. 将堆内存中的地址值赋值给左边的变量

当main方法也出栈的时候,此时没东西指向堆内存。故会自动销毁

类中的方法是先在方法区中加载,然后随着顺序执行的时候。

调用该方法才进栈

  • 两个对象的内存图

和一个对象的内存图区别就是,方法区只需要加载一次!堆内存new几次开辟几次

  • 两个引用指向同一个对象

this的内存原理

thi作用:区分局部变量和成员变量

this本质:所在方法调用者的地址值

字符串相关底层原理

  • 字符串存储的内存原理

    1. 直接赋值会复用字符串常量池中的字符串

      串池中有才会复用,没有则会在串池中新建对应的字符串

    2. new出来的不会复用,而是在堆内存中开辟一个新空间

  • ==号比较的到底是什么

    1. 基本数据类型比较的是数据值

    2. 引用数据类型比较的是地址值

    一般用equals方法来比较而不是==

  • 字符串拼接的底层原理

    1. 等号右边没有变量

    2. 等号右边有变量

      • 在jdk8之前

      • 在jdk8之后

        jvm会先预估字符串的长度,然后创建数组字符串,然后把数组变成字符串

      综上!字符串不建议直接用+这样即使底层有优化但是还是很慢非常影响效率!一般用StringBuilder进行拼接

  • StringBuilder源码分析

  1. 默认创建一个长度为16的字节数组
  2. 添加内容长度小于16直接保存
  3. 添加内容大于16扩容!(原容量*2+2)
  4. 如果还是不够就以实际为准

继承的内存图

例:

public class Fu{
	String name;
    int age;
}
public class Zi extends Fu{
    String game;
}
public class Tesx{
    public static void main(String[] args){
        Zi z=new Zi();
        sout(z);
        z.name="张三";
        z.age=23;
        z.game="王者荣耀";
        sout(z.game);
    }
}

执行过程:

  1. 在方法区中加载Test.clss文件

  2. 在栈内存中加载Main方法入栈,开始执行main方法内部代码

  3. Zi z=new Zi();在方法区中加载Zi.clss和Fu.clss文件

    在对内存中开辟空间并将地址值赋值给z

    注:因为Fu和Zi是继承关系,就会在这个开辟的空间中分割出俩个小空间,一半为父类的一半为子类的

  4. 输出z的地址值

  5. z.name="张三" 先在子类空间中找name这个成员变量,找不到了再去父类空间中查找并且赋值

  6. z.age=23z.game同理

  7. 输出z.game的内容

  8. main方法执行完毕,出栈

  9. 对内存因为没有指针指向了,自动销毁

  10. 方法区自动销毁

图:

在继承体系中成员方法的继承原理:

在继承中,子类只能继承父类中的非私有成员方法

例:

假设一个继承结构:A继承B,B继承C

A a=new A();
a.C中方法();

java会从最顶级父类开始:创建一个虚方法表

虚方法表内存储的方法要求:非private,非static,非final,这些方法统称虚方法

java会把虚方法抽取并放入虚方法表中,并把虚方法表交给子类,子类的虚方法表就是在父类的虚方法表的继承上添加自己的虚方法

如图:

方法重写,如果重写的是虚方法那么子类的虚方法会覆盖从父类中获得的那个虚方法

BigInteger底层存储方式

  1. 对于计算机来说,没有数据类型的概念,都是01组成的
  2. 数据类型是编译语言自己定义的

下图是BigInteger的源码:

image

其中signum存储的是正负号

mag数字存储的是数据,BigInteger在存储数据的时候会将数据拆分为多段,然后每段都单独放入数组中

存储方式图:

把数据转化为二进制补码,以32位一组,转化位十进制,其中符号位单独存储

  • BigInteger的存储上限

数组中最多存储元素个数:21亿多

数组中每一位可以表示的数:42亿多

BigInteger能表示的最大数为:42亿的21亿次方

BigDecimal底层存储方式

image

如图:将字符串拆解并转化为ACII表的数值存入byte数组中进行存储,符号当然也占一个位