java基础篇


前言

缺反射和内部类

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。--------王小波


一、JAVA核心知识点


关于 JVM JDK 和 JRE 最详细通俗的解答

JVM:Java 虚拟机(JVM)是运⾏ Java 字节码的虚拟机。字节码和不同系统的 JVM 实现是 Java 语⾔“⼀次编译,随处可以运⾏”的关键所在。

什么是字节码?采⽤字节码的好处是什么?

在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的⽂件),它不⾯向任何特定的处理器,只⾯向虚拟机。Java 语⾔通过字节码的⽅式,在⼀定程度上解决了传统解释型语⾔执⾏效率低的问题,同时⼜保留了解释型语⾔可移植的特点。所以 Java 程序运⾏时比较⾼效,⽽且,由于字节码并不针对⼀种特定的机器,因此,Java 程序⽆须重新编译便可在多种不同操作系统的计算机上运⾏

Java 程序从源代码到运⾏⼀般有下⾯ 3 步:
在这里插入图片描述
Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。

总结:

简单来说就是JDK是Java的开发工具,JRE是Java程序运行所需的环境,JVM是Java虚拟机.它们之间的关系是JDK包含JRE和JVM,JRE包含JVM.

java运行的三个步骤

编写/编译/解释,编译期间检查Java语法,如果出现错误,则不会产生类文件,
解释期间则将类文件装进JVM中,解释成二进制文件,然后机器才能识别运行.

java" HelloWorld "的执行过程以及原理。

public class HelloWorld {
    public static void main(String[] args)
    {    
            String  s ;
            s = “Hello World!;
            System.out.println(s); 
    }
}

HelloWorld的具体执行过程:

1.执行HelloWorld.java文件,生成HelloWorld.class字节码文件;
2.虚拟机执行HelloWorld.class,将这个类加载到内存中(即方法区的类代码区中);
3. 虚拟机通过类找到HelloWorld的主方法(程序的入口方法),访问权限为public(公有可用),虚拟机传递String类型参数的地址到主方法的args中去,并在栈区为args开辟内存空间,返回一个void的返回值;
4.定义一个String(标准类库中的)类型的变量(在栈区开辟空间)s,s的值不确定(垃圾值,编译无法通过);
5.s = “Hello World!”,对象“Hello World!”在方法区的常量数据区开辟空间,属性即为:Hello World!,方法即为:toString(),变量s存放对象“Hello World!”的地址;
6.虚拟机找到标准类库中的System.class类并加载到内存中(即方法区的类代码区中),System.out为标准字节输出流对象(),并调用println()方法将变量s的值打印到屏幕上。

JAVA和C++的区别

  • Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。
  • Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。
  • Java 支持自动垃圾回收,而 C++ 需要手动回收。
  • Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
  • Java 不支持操作符重载,而 C++ 可以。
  • Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。

⾯向对象和⾯向过程的区别

什么是面向对象?
对比面向过程,是两种不同的处理问题的角度
面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象)、及各自需要做什么
比如:洗衣机洗衣服
面向过程会将任务拆解成一系列的步骤(函数),1、打开洗衣机----->2、放衣服----->3、放洗衣粉------>4、清洗----->5、烘干
面向对象会拆出人和洗衣机两个对象:
人:打开洗衣机 放衣服 放洗衣粉
洗衣机:清洗 烘干
从以上例子能看出,面向过程比较直接高效,而面向对象更易于复用、扩展和维护

Java ⾯向对象编程三⼤特性: 封装 继承 多态

  • 封装
    封装就是隐藏对象的属性和实现细节,仅对外公开接口
  • 继承
    继承就是子类继承父类的特征和行为

关于继承如下 3 点请记住:

  1. ⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有属性和⽅法⼦类是⽆法访问,只是拥有。
  2. ⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。
  3. Java只支持单继承,不支持多继承。
  • 多态
    多态是同一个行为具有多个不同表现形式或形态的能力。

在Java中实现多态的三个必要条件:继承、重写、向上转型(指在多态中需要将子类的引用赋给父类对象)
在 Java 中有两种形式可以实现多态:继承(多个⼦类对同⼀⽅法的重写)和接⼝(实现接⼝并覆盖接⼝中同⼀⽅法)。

关键字


final 关键字

final 关键字主要用于修饰类,变量,方法。

  1. 类:被 final 修饰的类不可以被继承
  2. 方法:被 final 修饰的方法不可以被重写,但是可以重载
  3. 变量:被 final 修饰的变量是基本类型,变量的数值不能改变;被修饰的变量是引用类型,变量便不能在引用其他对象,但是变量所引用的对象本身是可以改变的。

final finally finalize区别

  • final 主要用于修饰类,变量,方法
  • finally 一般作用在 try-catch 代码块中,在处理异常的时候,通常我们将一定要执行的代码方法 finally 代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
  • finalize 是一个属于 Object 类的一个方法,该方法一般由垃圾回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用 finalize() ,回收垃圾,但Java语言规范并不保证 inalize 方法会被及时地执行、而且根本不会保证它们会被执行。

static关键字

static 关键字的主要用途就是方便在没有创建对象时调用方法和变量和优化程序性能

初始化顺序:

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。如果存在继承关系的话,初始化顺序为父类中的静态变量和静态代码块——子类中的静态变量和静态代码块——父类中的实例变量和普通代码块——父类的构造函数——子类的实例变量和普通代码块——子类的构造函数

this关键字

  1. this 关键字可用来引用当前类的实例变量。主要用于形参与成员名字重名,用 this 来区分。
  2. this 关键字可用于调用当前类方法。
  3. this() 可以用来调用当前类的构造函数。(注意: this() 一定要放在构造函数的第一行,否则编译
    不通过)

super关键字

  1. super 可以用来引用直接父类的实例变量。和 this 类似,主要用于区分父类和子类中相同的字段
  2. super 可以用来调用直接父类构造函数。(注意: super() 一定要放在构造函数的第一行,否则编译不通过)
  3. super 可以用来调用直接父类方法。

两者区别:

  • 相同点:
    1. super() 和 this() 都必须在构造函数的第一行进行调用,否则就是错误的
    2. this() 和 super() 都指的是对象,所以,均不可以在 static 环境中使用。
  • 不同点:
    1. super() 主要是对父类构造函数的调用, this() 是对重载构造函数的调用
    2. super() 主要是在继承了父类的子类的构造

##运算符

  • &&和&
    && 和 & 都可以表示逻辑与,但他们是有区别的,共同点是他们两边的条件都成立的时候最终结果才是 true ;不同点是 && 只要是第一个条件不成立为 false ,就不会再去判断第二个条件,最终结果直接为 false ,而 & 判断的是所有的条件。
  • ||和|
    || 和 | 都表示逻辑或,共同点是只要两个判断条件其中有一个成立最终的结果就是 true ,区别是 || 只要满足第一个条件,后面的条件就不再判断,而 | 要对所有的条件进行判断。

类型转换

  • 隐式(自动)类型转换:从存储范围小的类型到存储范围大的类型。
    byte → short(char) → int → long → float → double
  • 显示(强制)类型转换(精度损失):从存储范围大的类型到存储范围小的类型。
    double → float → long → int → short(char) → byte

自动装箱与拆箱

  • 装箱:将基本类型用包装器类型包装起来
  • 拆箱:将包装器类型转换为基本类型

注:Integer 、 Short 、 Byte 、 Character 、 Long 这几个类的 valueOf 方法的实现是类似的。
这几个的valueOf 方法,如果数值在[-128,127]之间,便返回指向 IntegerCache.cache 中已经存在的对象的引用,否则创建一个新的 Integer对象
Double 、 Float 的 valueOf 方法的实现是类似的。然后是 Boolean 的 valueOf 方法是单独一组的。
Double、 Float 的 valueOf 方法,每次返回都是重新 new 一个新的对象。

对于大类型来说关心的是引用传递但是在[-128,127]是值传递

==拓展:在hasp表中 包装类(Integer等)一律按值传递,增删改查在使用时都是O(1) ==
非基础类型的key按引用传递
在这里插入图片描述
非基础类型按引用传递 只存内存地址 所以Hsah<Node,Node>的key和value是8字节

String(不是基本数据类型)

在 Java 8 中,String 类中使⽤ final 关键字修饰字符数组来保存字符串,所以 String对象是不可变的。

public final class String implements java.io.Serializable, Comparable<String>,
CharSequence {
 private final char value[];
}

String不可变性的好处:

  • 可以缓存 hash 值()
    因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key 。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
  • 常量池优化
    String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
  • 线程安全
    String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

String 类的常用方法

  • length() :返回字符串长度
  • charAt() :返回指定索引处的字符
  • substring() :截取字符串
  • trim() :去除字符串两端空白
  • split() :分割字符串,返回一个分割后的字符串数组。
  • replace() :字符串替换。
  • indexOf() :返回指定字符的索引。
  • toLowerCase() :将字符串转成小写字母。
  • toUpperCase() :将字符串转成大写字符。

String和StringBuffer、StringBuilder的区别是什么

1.可变性
String 不可变, StringBuilder 和 StringBuffer 是可变的
2.线程安全性
String 由于是不可变的,所以线程安全。 StringBuffer 对方法加了同步锁或者对调用的方法加
了同步锁,所以是线程安全的。 StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
3.性能
StringBuilder > StringBuffer > String

重载和重写的区别

重载: 发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分(因为调用时不能指定类型信息,编译器不知道你要调用哪个函数)。
重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。

== 和 equals 的区别

  • ==
    对于基本数据类型== 比较的是值;对于引用数据类型, == 比较的是内存地址。
  • equals
    对于没有重写 equals 方法的类, equals 方法和 == 作用类似;对于重写过 equals 方法的类,equals 比较的是值。

说明:

  • String 中的 equals ⽅法是被重写过的,因为 object 的 equals ⽅法是比较的对象的内存地址,⽽ String 的 equals ⽅法比较的是对象的值。
  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引⽤。如果没有就在常量池中重新创建⼀个 String 对象。

hashCode 与 equals

为什么重写equals方法后,hashCode方法也必须重写
第一,在 HashSet 等集合中,不重写 hashCode 方法会导致其功能出现问题;
第二,可以提高集合效率。
为什么两个对象有相同的 hashcode 值,它们也不⼀定是相等的
因为 hashCode() 所使⽤的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode 。

为什么 Java 中只有值传递?

  • 值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  • 引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

从定义中可以明显看出,区分是值传递还是引用传递主要是看向方法中传递的是实际参数的副本还是实际参数的地址。

抽象类和接口的对比

总结⼀下 jdk7~jdk9 Java 中接⼝概念的变化:

  1. 在 jdk 7 或更早版本中,接⼝⾥⾯只能有常量变量和抽象⽅法。这些接⼝⽅法必须由选择实现接⼝的类实现。
  2. jdk 8 的时候接⼝可以有默认⽅法和静态⽅法功能。
  3. Jdk 9 在接⼝中引⼊了私有⽅法和私有静态⽅法。

在这里插入图片描述

在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤

Java 程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中“没有参数的构造⽅法”。因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找不到没有参数的构造⽅法可供执⾏。解决办法是在⽗类⾥加上⼀个不做事且没有参数的构造⽅法。

IO流

Java 中 IO 流分为⼏种

按照流的流向分,可以分为输⼊流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的⻆⾊划分为节点流和处理流。

Java I0流的40多个类都是从4个抽象类基类中派生出来的。

InputStream:字节输入流 Reader:字符输入流
OutputStream:字节输出流 Writer:字符输出流

既然有了字节流,为什么还要有字符流?

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是⾮常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就⼲脆提供了⼀个直接操作字符的接⼝,⽅便我们平时对字符进⾏流操作。如果⾳频⽂件、图⽚等媒体⽂件⽤字节流比较好,如果涉及到字符的话使⽤字符流比较好。

BIO,NIO,AIO 有什么区别?

BIO :来到厨房,开始烧水NIO,并坐在水壶面前一直等着水烧开。
NIO:来到厨房,开AIO始烧水,但是我们不一直坐在水壶前面等,而是做些其他事,然后每隔几分钟到厨房看一下水有没有烧开。
AIO:来到厨房,开始烧水,我们不一直坐在水壶前面等,而是在水壶上面装个开关,水烧开之后它会通知我。

JAVA异常

异常主要分为 Error 和 Exception 两种

  • Error: Error 类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理。
  • EXception: Exception 以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
    异常框图
  • 非检查异常(unckecked exception):该类异常包括运行时异常(RuntimeException及其子类)和错误(Error)。编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有 try-catch 捕获它,也没有使用 throws 抛出该异常,编译也会正常通过。因为这样的异常发生的原因很可能是代码写的有问题。
  • 检查异常(checked exception):一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一般包括几个方面:
    1. 试图在文件尾部读取数据
    2. 试图打开一个错误格式的 URL
    3. 试图根据给定的字符串查找 class 对象,而这个字符串表示的类并不存在

try{}catch(){}finally{} 的执行顺序

  1. 先执行 try 中的语句,包括 return 后面的表达式;
  2. 有异常时,执行 catch 中的语句,包括 return 后面的表达式,无异常跳过 catch 语句;
  3. 然后执行 finally 中的语句,如果 finally 里面有 return 语句,执行 return 语句,程序结束;
  4. finally{} 中没有 return 时,无异常执行 try 中的 return ,如果有异常时则执行 catch 中的return 。前两步执行的 return 只是确定返回的值,程序并未结束, finally{} 执行之后,最后将前两步确定的 return 的返回值返回。

Throw 和 throws 的区别:

  • 位置不同
  1. throws 用在函数上,后面跟的是异常类,可以跟多个;而 throw 用在函数内,后面跟的是异常对象。
  • 功能不同:
  1. throws 用来声明异常,让调用者只知道该功能可能出现的问题,可以给出预先的处理方式;throw 抛出具体的问题对象,执行到 throw,功能就已经结束了,跳转到调用者,并将具体的问题对象抛给调用者。也就是说 throw 语句独立存在时,下面不要定义其他语句,因为执行不到。
  2. throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw 则是抛出了异常,执行 throw 则一定抛出了某种异常对象。
  3. 两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

JAVA泛型

Java 泛型是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

  • 泛型擦除(这是面试考察泛型时经常问到的问题)
    Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的 Java 字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的 List和 List等类型,在编译之后都会变成 List。JVM 看到的只是 List,而由泛型附加的类型信息对 JVM 来说是不可见的。

既然存在类型擦除,那么Java是如何保证在 ArrayList 添加字符串会报错呢?

Java编译器是通过先检查代码中泛型的类型,然后在进行类型擦除,再进行编译。

JAVA序列化

  • 序列化:将对象写入到IO流中

  • 反序列化:从IO流中恢复对象

  • 序列化的意义:将Java对象转换成字节序列,这些字节序列更加便于通过网络传输或存储在磁盘上,在需要时可以通过反序列化恢复成原来的对象。

  • 实现方式:

    1. 实现Serializable接口
    2. 实现Externalizable接口
  • 序列化的注意事项:

    1. 对象的类名、实例变量会被序列化;方法、类变量、 transient 实例变量都不会被序列化。
    2. 某个变量不被序列化,可以使用 transient 修饰。
    3. 序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
    4. 反序列化时必须有序列化对象的 class 文件。

深拷贝与浅拷贝

  • 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,两个引用指向两个对象,但对象内容相同。
  • 浅拷贝:对基本数据类型进行值传递,对引用数据类型复制一个引用指向原始引用的对象,就是复制的引用和原始引用指向同一个对象。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值