Java基础 学习笔记(一)
前言
大二大三在课堂上浅学了一点点,现在再从头仔细打打基础。
主要参考:JavaGuide(https://javaguide.cn/);《Java核心技术卷》;《深入了解Java虚拟机》
以这些为主,同时会看相关博客文章,不同的文章对同一个知识点的描述远近高低各不同,更加帮助理解记忆。
基础概念与常识
认识JVM,JRE,JDK
-
JVM
Java VM是运行Java字节码的虚拟机。有针对不同系统(windows, linux, maxOS)的特定实现,目的是使用相同的字节码。“Write once, Run Anywhere.”。作用是加载编译器编译好的字节码,并将其解释成机器码。
我们平时接触的是Sun公司的HotSpot VM,JVM有很多种,只要满足JVM规范,HotSpot VM只是JVM规范的一种实现。
-
JRE
Java Runtime Environment,可以让计算机系统运行Java应用程序(Java Application)。JRE内部包括Java虚拟机(JVM),以及标准类别函数库。
JRE只能够运行程序,不能用来创建程序。
-
JDK
Java Development Kit,Java的开发工具包,是整个Java开发的核心,包含了Java的运行环境(JRE)和Java工具。
(JDK包含的基本组件包括:
javac – 编译器,将源程序转成字节码;
jar – 打包工具,将相关的类文件打包成一个文件;
javadoc – 文档生成器,从源码注释中提取文档;
jdb – debugger,查错工具;
java – 运行编译后的java程序(.class后缀的);
appletviewer:小程序浏览器,一种执行HTML文件上的Java小程序的Java浏览器;
Javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件;
Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义;
Jconsole: Java进行系统调试和监控的工具。)
认识字节码
首先了解Java程序从源代码到运行的过程(Java是编译与解释共存的语言):
.java文件----> 经过javac编译 ----> .class文件(字节码文件)---->JVM
类加载器加载字节码文件,随后解释器(&JIT)解释 ----> 机器码 ---->OS
所以.class字节码文件,只供虚拟机使用,且不针对特定操作系统。在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可以指的特点。
PS:在.class字节码---->机器码的过程中,类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度相对比较慢。而且有些方法和代码块是经常需要被调用的。随后引进JIT,just in time compilation,编译器,属于运行时编译,当JIT完成一次编译后,会将字节码对应的机器码保存下来,下次可以直接使用。机器码的运行效率高于解释器。
Java是编译与解释共存的语言
编译型语言源代码(C, C++,GO等)---->编译器(一次性编译完毕)---->机器码---->OS 开发效率较低,执行速度较快
解释型语言源代码(Python,JavaScript,PHP等)---->解释器(逐句解释)---->机器码---->OS 开发效率较快,执行速度较慢
Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class
文件),这种字节码必须由 Java 解释器来解释执行。改善了编译语言的效率,混合了编译语言与解释型语言的优点。
基本语法
静态方法为什么不能调用非静态成员
静态方法属于类,不属于类的某个对象,在类加载时会分配内存,可以通过类名直接访问(类名.对象名)。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象访问。
在类的非静态成员不存在的时候,静态成员已经存在了,此时不能调用在内存中还不存在的非静态成员。
实例方法调用,实例名.方法名
重载与重写
重载 | 重写 | |
---|---|---|
类关系 | 同一个类中 | 继承关系父子类之间 |
参数列表 | 必须修改(个数,类型或顺序) | 不可变 |
返回类型 | 可修改 | 可以不相同,但是必须是父类返回值的派生类 |
方法名 | 不可修改 | 不可变 |
异常 | 可修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可修改 | 一定不能做更严格的限制(可以降低限制) |
方法的重写Overrid,重写Overload是Java多态性的不同表现。
方法重载是一个类中定义了方法名相同,参数数量不同或数量相同儿类型和次序不同。是一个类的多态性表现。
方法重写是在子类中,写一个与父类被重写的方法名称相同,参数个数类型一样,返回值也一样的方法,仅方法体内部与父类方法不同。是子类与父类的多态性表现。
重载,同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。 比如,同一个类中,写不同的构造函数用于初始化不同的参数。发生在编译期。
重写,外壳不变,核心重写。子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。发生在运行期。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,抛出 IOException 异常或者 IOException 的子类异常。
==和equals( )的区别
==
- 对于基本数据类型,应使用 == 来判断是否相等,比较的的是值。
- 对于引用数据类型(类,接口,数组),当使用 == 时,比较的是引用对象的内存存放地址。
equals( )
-
存在于Object类中,判断两个对象是否相等。
-
equals( )没有被重写时,比较的是两对象的内存地址,相当于==。使用的默认是
Object
类equals()
方法。 -
equals( )被重写,使其比较**对象的内容(属性)**而不是比较对象的地址,如String,Integer等类中,就重写了此方法。
String a = new String("ab"); // a 为一个引用 String b = new String("ab"); // b为另一个引用,对象的内容一样 String aa = "ab"; // 放在字符串常量池中 String bb = "ab"; // 从字符串常量池中查找 System.out.println(aa == bb);// true System.out.println(a == b);// false System.out.println(a.equals(b));// true System.out.println(42 == 42.0);// true
【关于创建String类型的对象,虚拟机会在字符串常量池(jdk7前在方法区,之后在堆中)中查找有没有要和要创建的值相同的对象,若有就将其赋给当前引用;若没有,就在字符串常量池中创建一个此值的对象。】
hashCode( )与equals( )
hashSet 如何添加对象 & hashSet 如何检查重复 & hashCode( )的作用
当把一个对象加入hashSet时,hashSet会先计算对象的hashCode值(哈希码,int整数,也称散列码),来判断决定对象加入的(索引)位置。同时会hashSet中其他已加入的对象的哈希码作比较。如果没有相等的hashCode,hashSet会假设对象没有重复出现,即将此对象加入hashSet;若存在相同的hashCode,将调用 equals( )方法进一步判断两个对象是否真的相同。如果经equals( )判断相同,则对象添加失败;若不同,会将此对象散列到其他位置。
–>减少了equals次数,提高了执行速度。
两个对象有相同的hashCode值,也不一定相等
hashCode值由所使用的哈希算法决定,存在不同对象传回相同的哈希值(哈希碰撞)的可能性,越糟糕的哈希算法越容易碰撞,但这也与数据之域分布的特性有关。
两个对象 hashCode相等 ----> equals( ) 返回true ----> 两个对象相等
两个对象 hashCode相等 ----> equals( ) 返回false ----> 两个对象不等
两个对象 hashCode不等 ----> 两个对象不等
所以,当我们重写equals( )方法时,也必须要重写hashCode( )。
重写equals( )后,没有重写hashCode( ),会导致equals( )判断是相等的两个对象,hashCode值却不相等。
可变长参数
在调用方法时,传入的参数个数可以不固定。
/**
* 微信搜 JavaGuide 回复"面试突击"即可免费领取个人原创的 Java 面试手册
*
* @author Guide哥
* @date 2021/12/13 16:52
**/
public class VariableLengthArgument {
public static void printVariable(String... args) {
for (String s : args) {
System.out.println(s);
}
}
public static void printVariable(String arg1, String arg2) {
System.out.println(arg1 + arg2);
}
public static void main(String[] args) {
printVariable("a", "b");
printVariable("a", "b", "c", "d");
}
}
Java的可变参数编译后会被转换成一个数组。只能作为方法的最后一个参数,前边可以有也可以没有任何参数。当方法重载时,会优先匹配固定参数的方法。