lukaliou123 / lukaliou123.github.io

lukaliou123在2022年的面试用知识点总结
Other
5 stars 0 forks source link

JAVA基础篇1 #3

Open lukaliou123 opened 2 years ago

lukaliou123 commented 2 years ago

1.JVM

JVM是运行Java字节码的虚拟机。JVM有针对不同系统的特定实现,目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的JVM实现是Java语言“一次编译,随处可以运行”的关键所在。

JVM并不是只有一种,只要满足JVM规范,每个在公司,组织,或者个人都可以开发自己的专属JVM。

2.JDK和JRE

JDK是Java Development Kit缩写,他是功能齐全的JAVA SDK。拥有JRE的一切,还有编译器(javac)和工具(javadoc和jdb)。能创建和编译程序。

JRE(Java Running Environment)是Java运行时环境。它是运行已编译Java程序所需的内容的集合,包括Java虚拟机(JVM),Java(类库),java命令和其它的一些基础构建。但是不能用于创建新程序

lukaliou123 commented 2 years ago

3.什么是字节码?采用字节码的好处是什么?

在Java中,JVM可以理解的代码就叫做字节码(及扩展名为.class的文件),它不面向任何特定的处理器,只面向虚拟机。Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。而且,由于字节码并不针对一种特定的机器,因此Java程序无需重新编译便可在多种不同操作系统的计算机运行

Java程序从源代码到运行的过程如下图所示: image 我们需要格外注意.class->机器码这一步。在这一步JVM类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了JIT(just-in-time compilation)编译器,而JIT属于运行时编译。当JIT编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。机器码的运行效率高于Java解释器,这就是为什么Java是编译与解释共存的语言。

4. 为什么说 Java 语言“编译与解释并存”?

高级编程语言按照程序的执行方式分为两种: 编译型:通过编译器将源代码一次性翻译成可被该平台执行的机器码。编译语言执行速度快,开发效率低。常见的有C,C++,Go, Rust 解释型:会通过解释器一句一句将代码释放为机器代码后再执行。解释型语言开发效率快,执行速度慢。常见的有Python,JS,PHP image 这是因为Java语言既具有编译型语言的特征,也具有解释型语言的特征。 因为Java程序要经过先编译,后解释两个步骤,由Java编写的程序需要先经过编译步骤,生成字节码(.class文件),这种字节码必须由Java解释起来解释执行。

lukaliou123 commented 2 years ago

5.重载和重写的区别

重载(OverLoad)就是同样的一个方法能够根据输入数据的不同,做不同处理 重写(OverRide)就是当子类继承字符类的相同方法,输入数据一样,但要做出有别于父类的响应式,覆盖父类方法 1645588607(1)

lukaliou123 commented 2 years ago

6.泛型

原生:实际上一开始是给collection用的,一开始的list存的东西都是object,需要我们手动转,现在可以直接放就是ArrayList使用了泛型的结果

编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型。比如 ArrayList persons = new ArrayList() 这行代码就指明了该 ArrayList 对象只能传入 Person 对象,如果传入其他类型的对象就会报错 1691286209537

Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型。 提供了编译器的类型安全,确保你只能把正确类型的对方放入集合中,避免在运行时出现classCastException. 操作的数据类型被指定为一个参数。

Java的泛型是伪泛型,这是因为Java在运行期间,所有的泛型信息都会被擦掉,这也是通常所说的类型擦除。 泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个List来表示。

泛型的优点

1.类型安全:泛型的主要目标是提高 Java 程序的类型安全。通过知道使用的类型的限制,编译器可以进行更多的编译时检查。 2.代码重用:你可以写一个与类型无关的代码,该代码可以用于各种类型的对象。这样我们就可以重用同一段代码。

使用泛型的关键字是尖括号<>。在编程时,如果使用泛型类,需要在创建对象的时候指定类中泛型的具体类型。

常用的通配符:

T E K V ? ?是不确定的java类型 T是具体的一个Java类型 KV表示Key和Value E代表element

上界和下界 限定通配符

List<? extends T>可以接受任何继承自T的类型的List,而List<? super T>可以接受任何T的父类构成的List。例如List<? extends Number>可以接受List或List

泛型类 image

泛型接口 image

泛型方法 image

几个实际的使用例子

1.泛型在集合类中的使用image 2.泛型类的使用 QV62F98NX(8$VYY34$}HLH3 3.泛型方法的使用QGN`RU ZZ~PF1(UURAZD5KN

可能出现的问题:new List<new LinkedList>是否会报错?

会,尖括号中应该是类或接口的类型,不能是具体的对象 QDAYSJOIJBY1B~~GHGDSZ2D

lukaliou123 commented 2 years ago

7.equals()和==区别

== 对于基本类型和引用类型的作用效果是不同的: 对于基本数据类型来说,== 比较的是值。 对于引用数据类型来说,== 比较的是对象的内存地址。

equals() 作用不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类。

equals() 方法存在两种使用情况: 类没有覆盖 equals()方法 :通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。

类覆盖了 equals()方法 :一般我们都覆盖 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。

lukaliou123 commented 2 years ago

8.Java面向对象编程三大特性:封装 继承 多态

1.封装 Encapsulation: /ɪnˌkæpsjʊˈleɪʃən/(in-kap-syoo-LAY-shuhn) 封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

2.继承 Inheritance: /ɪnˈherɪtəns/(in-HER-i-tuhns) 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

注意:子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。

3.多态 Polymorphism: /ˌpɒlɪˈmɔːfɪzəm/(pol-ee-MOR-fiz-uhm) 多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;

lukaliou123 commented 2 years ago

9.字符型常量和字符串常量的区别?

1.形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符 2.含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置) 3.占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节

10.Java中基本数据类型的大小 image

lukaliou123 commented 2 years ago

10.为什么静态对象访问非静态对象是非法操作

这个需要结合 JVM 的相关知识,主要原因如下: 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。 在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作

lukaliou123 commented 2 years ago

11.String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的

1.保存字符串的数组被final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法

  1. String类被final修饰导致其不能被继承,进而避免了子类破坏String不可变
  2. 线程安全:String中的对象是不可变的,也可以理解为常量,线程安全 StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以线程是安全的 4.性能: String:每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象

StringBuffer:StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用

StringBuilder:比Buffer高10-15的性能,但是进程不安全

字符串拼接用“+” 还是 StringBuilder?

1691282817805 上述对应的字节码如下 image 可以看出,字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。 不过,在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象。 如果直接使用 StringBuilder 对象进行字符串拼接的话,就不会存在这个问题了。 不过JDK9之后就解决了

lukaliou123 commented 2 years ago

12.接口和抽象类的区别是什么

相同点 1.都不能被实例化 2.接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点 (1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。 (2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。 (3)接口强调特定功能的实现,而抽象类强调所属关系。 (4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。 抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值; 抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

抽象类存在的意义

1.为子类提供了一个通用的类型:抽象类最重要的作用就是为一系列的子类提供了一个公共的、高级的概念。一个抽象类是其子类的模板,提供了一种通用的类型,增强了程序的扩展性。

2.提供了默认行为:抽象类中可以有实现的方法(即含有方法体的方法),子类可以直接继承这些方法。而接口中只能定义抽象方法,即只能定义方法的签名,不能实现方法的功能。因此,如果你想要定义一些方法,并为它们提供默认的实现,那么你应该使用抽象类。

3.定义了子类的核心架构:抽象类是一种强制性规范,一旦子类继承了某个抽象类,就必须实现其中的抽象方法。这样,无论子类如何变化,基本的行为都已经在抽象类中定义好了。

4.可以定义构造函数和字段:抽象类中可以定义构造函数,子类通过调用这些构造函数可以实现更多的功能。同时,抽象类中可以定义字段(即变量),而接口中只能定义静态不可变的字段(即常量)。

lukaliou123 commented 2 years ago

13.关于final关键字的一些总结

1.对于一个final变量,如果是基本数据类型的变量,在其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象 2.用final修饰一个类时,表明这个类不能再被继承。Final类中所有的成员方法都会被指定为final方法 3.使用final方法的原因有二:1.把方法锁定,以防任何继承类修改它的定义;2.效率

lukaliou123 commented 2 years ago

14.值传递和引用传递的区别

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

15.为什么Java中只有值传递

  1. 一个方法不能修改一个基本数据类型的参数
  2. 一个方法可以改变对象参数的状态
  3. 一个方法不能让对象参数引用的一个新的对象 实际上java的引用对象保存的是一个地址,java仍然是用值传递来将这个地址传递给其他对象
lukaliou123 commented 2 years ago

16.finalize final finally区别

1.final是修饰符,可以修饰类,变量,方法。修饰类表示该类不能被继承、修饰方法表示该方法不能被子类重写(覆盖)、修饰变量表示该变量是一个常量不能被重新赋值 2.finally是一个关键词,一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码放在finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码 3.finalize是一个方法,属于object类中的一个方法,而object类是所有类的父类,java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作

16.1.try和finally都有return,怎么执行?

在Java中,如果try和finally语句块中都有return语句,那么程序会先执行try语句块的return语句,但是并不立即返回结果,而是先去执行finally语句块的代码,等finally语句块的代码执行完之后,再返回try语句块的结果image 1.首先,打印出"In try block"。

2.然后,程序遇到return 1;语句,但并不立即返回结果,而是进入finally语句块。

3.在finally语句块中,打印出"In finally block"。

4.遇到return 2;语句,这时候程序就返回结果2,而不是try语句块中的1。

所以,虽然try和finally语句块中都有return语句,但是最终的返回结果是finally语句块中的返回结果。这种设计主要是为了确保finally语句块中的代码无论何时都能得到执行。

lukaliou123 commented 2 years ago

17.反射

image Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法

lukaliou123 commented 1 year ago

18.break flag

1.break:在没有标签的情况下,break只会跳出最内层的循环。 2ZUUPAY3D_5V7PVBY`_W %K

在这个例子中,break语句只会终止内部的j循环。当j等于5时,程序只会跳出内部的j循环,然后i循环会继续执行下一轮。

2.break标签(break flag):break标签可以跳出指定的外层循环。 1689690713059

在这个例子中,当j等于5时,break outer语句会使程序跳出标签outer指定的外层i循环,而不仅仅是内层的j循环。也就是说,break outer终止了整个外层循环,而不会像普通的break那样只终止最内层的循环。

所以,对于你的问题,break标签可以跳出指定的外层循环,而不是回到外层循环开始的地方继续;而不带标签的break只会跳出最内层的循环

lukaliou123 commented 1 year ago

19.几个new对象的方法

1.使用new关键字:这是最常见的创建对象的方式。例如:Person person = new Person();

2.使用Class类的newInstance方法:首先你要有一个类的Class对象,这可以通过类的全名和Class的forName静态方法获取,然后通过newInstance方法来创建这个类的对象,例如:Person person = (Person) Class.forName("Person").newInstance();这种方式常常用于反射。

3.使用Constructor类的newInstance方法:首先你要通过Class类的getConstructor方法获取一个Constructor对象,然后通过这个Constructor对象的newInstance方法来创建对象。

4.使用clone方法:如果一个类实现了Cloneable接口,那么它就可以调用clone方法来创建对象。

5.反序列化:如果一个类实现了Serializable接口,那么它可以被序列化和反序列化,反序列化就会创建新的对象。

6.通过Java反射机制。

lukaliou123 commented 1 year ago

20. 深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

1.浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。

2.深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

3.引用拷贝: 简单来说,引用拷贝就是两个不同的引用指向同一个对象。 image

简单来说引用拷贝根本没创建新对象

浅拷贝是在堆中创建了一个新的对象,然后复制了原始对象的引用类型的引用地址和基本类型的值,但是它们指向的仍是同一个引用类型的对象,所以如果我们改变这个引用类型的对象,所有引用这个对象的副本都会受到影响

引用拷贝则没有创建新的对象,而是创建了一个新的引用,指向同一个对象。因此,无论我们如何修改引用,它们始终指向同一个对象

lukaliou123 commented 1 year ago

21.注解(Annotation)

注解(Annotation)是Java语言提供的一种元数据的机制元数据指的是描述数据的数据,注解就是用于描述代码的信息。 注解不会改变你的程序逻辑,但是可以用来向编译器、虚拟机、框架等提供一些元信息。它们通常被用于以下几个方面:

1.编译检查:通过使用注解,可以使编译器知道某些信息从而进行编译时检查。例如,@Override 注解就表明这个方法必须重写父类的方法2.代码分析:注解可以帮助创建一些工具进行代码分析。例如,可以用注解标记过时的功能,或者生成文档。 3.代码增强:许多框架使用注解来执行代码增强,从而可以用更少的代码实现丰富的功能。例如,在Spring框架中,可以使用@Autowired 来实现自动依赖注入。 4.运行时处理:某些注解在运行时可用,可以被反射机制读取。例如,在JUnit中,可以使用@Test 注解标记测试方法。 1692013917415 在这个例子中,@Override 注解告诉编译器这个toString方法必须覆盖超类的方法。如果父类中没有toString方法,编译器会报错。

21.1如何自己写一个注解?

首先,创建一个新的注解是相当简单的。注解通常用 @interface 关键字来定义。在下面的例子中,我将创建一个名为 MyAnnotation 的注解: image

在这个例子中,MyAnnotation 是一个非常简单的注解,它只有一个属性 value,默认值为一个空字符串。

然后,我们就可以在代码中使用这个注解了 image

在上面的代码中,我在 TestClass 上使用了 MyAnnotation 并设置了 value 为 "This is a test"。

这只是最基本的使用,实际上,Java注解可以更复杂。你可以定义带有多个属性的注解,甚至可以定义可以注解其他注解的注解(也就是所谓的元注解)。

例如,我们经常会在Spring或J2EE这样的框架中见到的 @OverRide、https://github.com/entity、@Autowired、https://github.com/path 等就是注解。