【搞定面试】Java篇-Java基础

文章目录

1、为什么 Java 中只有值传递?

  • 术语解释:
    • 按值调用:表示方法接收的是调用者提供的值。即方法的得到的是变量的一份拷贝,而非原本的变量。【对引用:方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。】
    • 按引用调用:表示方法接收的是调用者提供的变量地址。方法的得到的是原本的变量的引用。【即引用的引用】

【答案】:Java程序设计语言只有按值调用。

  • 1)方法得到的是所有参数值的一个拷贝,方法不能修改传递给它的任何参数变量的内容。
  • 2)对对象采用的不是引用调用,实际上,对象引用是按值传递的。因为一个方法不能让对象参数引用一个新的对象。

2、==与equals(重要)

  • 前者的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型比较的是值,引用数据类型==比较的是内存地址)。

  • equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

    • 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
    • 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
  • String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。

3、你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法

  • 1)就规定来说:如果两个对象相等,则hashcode一定要相同的 ;两个对象相等,对两个对象分别调用equals方法都返回true

    • 1.1)Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数【哈希码】之后返回。【基本只有同一个对象才会有相同的哈希码】
      • 【hashCode() 的作用是获取哈希码,这个哈希码的作用是确定该对象在哈希表中的索引位置】
    • 1.2) Object 的 equals()方法。等价于通过“==”比较这两个对象。即判断两个对象的地址是不是相等。和hashcode方法的内涵一样。
  • 2) 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖。因为hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等【因为两个对象的hashcode()返回的值肯定不一样,那么根据规定,如果两个对象相等,则hashcode一定要相同的,所以这两个对象就不能相等,即使这两个对象我们重写的equal认为他们相等】

  • 附注:hashCode()与equals()的相关规定规定:

    • 如果两个对象相等,则hashcode一定也是相同的 **
    • 两个对象相等,对两个对象分别调用equals方法都返回true
    • 两个对象有相同的hashcode值,它们也不一定是相等的
    • 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖

4、为什么要有hashCode: hashcode 用于缩小查找成本。【能快速找到对象应该在的位置】

  • 我们以“HashSet,HashMap这些如何检查重复”为例子来说明为什么要有hashCode:
    • 1)当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现,那么就会加入(链表)。
    • 2)但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。
    • 3)这样我们就大大减少了equals的次数,相应就大大提高了执行速度。

5、String和StringBuffer、StringBuilder的区别?String为什么是不可变的?

1) String 对象不可变,StringBuilder和 StringBuffer 是可变的

  • String 类中使用final 修饰的字符数组保存字符串 private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中的字符数组 char[] value 没有用 final 关键字修饰,所以这两种对象都是可变的。
    AbstractStringBuilder.java
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;
    AbstractStringBuilder() {
    }
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

2) String 对象是常量不可变,所以线程安全;StringBuffer 对方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

//StringBuffer 类中 有方法没加同步锁:
//有的方法没加同步锁,但是它是通过超类方法将d转换为String后,
//通过调用StringBuffer insert(int,String)(子类覆盖了父类的这个方法)实现同步
    @Override
    public StringBuffer insert(int offset, double d) {
        // Note, synchronization achieved via invocation of StringBuffer insert(int, String)
        // after conversion of d to String by super class method
        // Ditto for toStringCache clearing
        super.insert(offset, d);
        return this;
    }

3)性能:每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

  • 对于三者使用的总结:
    • 操作少量的数据: 适用String
    • 单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
    • 多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer

6、什么是反射机制?

  • 反射:Java反射机制是在运行状态中,
    • 对于任意一个类,都能够知道这个类的所有属性和方法;
    • 对于任意一个对象,都能够调用他的任意一个方法和属性;
    • 这种动态获取信息以及动态调用对象方法的功能叫Java语言的反射机制
  • 反射机制提供了哪些功能?
    • 在运行时判定任意一个对象所属的类
    • 在运行时构造任意一个类的对象;
    • 在运行时判定任意一个类所具有的成员变量和方法;
    • 在运行时调用任意一个对象的方法;
    • 生成动态代理;

7、反射机制的优缺点:

  • 优点:运行期类型的判断,动态加载类,提高代码的灵活度
  • 缺点:性能问题:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
  • 补充说明:
    • 静态加载类:编译时刻加载的类,例如用new关键字创建的对象要通过编译器的静态检查,如果编译时该类不存在,那么使用该对象的类也无法通过编译。
    • 动态加载类:运行时刻加载的类,在编译的时候不会进行判断,只有在运行时才会进行判断,假设该类不存在,在运行时才会报错。
    • 反射使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)
    • 在运行期间,一个类,只有一个Class对象产生。

class Office{
	public static void main(String[] args){
		try{
            //forName()方法时动态加载类,即便编译时args[0]类名的类不存在,编译也是可以通过的,只是在运行时刻会抛出异常,返回的是Class类型
			Class c = Class.forName(args[0]);
			OfficeAble oa = (OfficeAble)c.newInstance();
			oa.start();
		}
		catch(Exception e){
			e.printStackTrace();
		}
		
	}

8、反射机制的应用场景有哪些?

反射是框架设计的灵魂。

在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。

举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性

9、什么是JDK?什么是JRE?什么是JVM?三者之间的联系与区别?

  • JDK: java开发工具箱。是Java Development Kit,它是功能齐全的Java SDK。它拥有JRE所拥有的一切,还有编译器(javac)和工具(如javadoc和jdb)。它能够创建和编译程序。

  • JRE(Java Runtime Environment)是Java运行时环境,用于运行已编译 的Java 程序,包括 Java虚拟机(JVM),Java类库,java命令(解释器)和其他的一些基础构件。但是,它不能用于创建新程序。

  • JVM: Java虚拟机,提供了内存管理/垃圾回收和安全机制等。负责将字节码转换为特定机器代码,这种独立于硬件和操作系统,正是java程序可以一次编写多处执行的原因

    • Java 程序从源代码到运行一般有下面3步:
    • 我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言
      • HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9引入了一种新的编译模式AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销。JDK支持分层编译和AOT协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。

10、什么是字节码?采用字节码的最大好处是什么?

  • Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行
  • 字节码的好处:Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。

11、java中的编译器、解释器和即时编译(JIT)【Java的编译与解释并存】:

  • Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,
  • 虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。
  • JVM中还存在着即时编译器优化代码执行,他会检测代码中的热点代码(即多次调用的方法或循环的代码块),这些代码如果每次都通过解释器解释执行无疑大大降低了运行效率,因此Jit编译器将他们编译成本地代码,则下次调用时就不需要解释器再次解释执行。

12、Java和C++的区别?

  • 都是面向对象的语言,都支持封装、继承和多态
  • Java不提供指针来直接访问内存,程序内存更加安全
  • Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。
  • Java 有自动内存管理机制(内存分配和垃圾回收),不需要程序员手动释放无用内存

14、重载和重写的区别?

  • 重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
  • 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。

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

1) 封装:从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。

2)继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

  • 关于继承如下 3 点请记住:
    • 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。(因为在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。)
    • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
    • 子类可以用自己的方式实现父类的方法。(重写)

3)多态:多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是方法的重载,通过参数列表的不同来区分不同的方法;运行时多态,也叫作动态绑定,一般是指在执行期间(非编译期间)判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。主要用于继承父类和实现接口时,父类引用指向子类对象。

16、 面向对象和面向过程的区别

  • 面向过程
    • 优点: 性能比面向对象高。因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发
    • 缺点: 没有面向对象易维护、易复用、易扩展
  • 面向对象【 易维护、易复用、易扩展】
    • 优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
    • 缺点: 性能比面向过程低
  • 解释:
    • 面向过程程序是由一系列相互独立的常量、变量和函数组成,他们之间没有明显的关联和隶属关系。任意函数之间可以相互调用,任意函数也可以使用全局的常量、变量,甚至可以改变全局的变量。
    • 面向对象程序的代码则是由一系列对象组成,每个对象包含其所属的常量、变量、方法,一般来说,引用对象内部状态与修改对象内部状态必须通过对象自身来进行,不能或不允许直接从对象的外部修改和控制对象的状态。这样,就可以避免面向过程程序中的变量发生不可预料的改变或超出其范围的变化。【所以容易维护】
      • 易维护:(封装)【把一个对象相关的都放在了一块】
      • 易复用:继承
      • 易扩展:继承、多态

17、 Java 语言有哪些特点?

  • 简单易学;
  • 面向对象(封装,继承,多态);
  • 平台无关性( Java 虚拟机实现平台无关性);
  • 自动内存管理
  • 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持);
  • 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  • 编译与解释并存【java,javac,JIT】
    • java解释器就是把在java虚拟机上运行的目标代码(字节码)解释成为具体平台的机器码的程序。即jdk或jre目录下bin目录中的java.exe文件,而javac.exe是编译器。
    • 运行java程序的过程是先用javac编译,然后用java解释。而一经编译成功后,就可以直接用java.exe随处解释运行了。
    • JIT编译器是JRE的一部分。原本的Java程序都是要经过解释执行的,其执行速度肯定比可执行的二进制字节码程序慢。为了提高执行速度,引入了JIT。在运行时,JIT会把翻译过来的机器码保存起来,以备下次使用。而如果JIT对每条字节码都进行编译,则会负担过重,所以,JIT只会对经常执行的字节码进行编译,如循环,高频度使用的方法等。它会以整个方法为单位,一次性将整个方法的字节码编译为本地机器码,然后直接运行编译后的机器码。

17、Oracle JDK 和 OpenJDK 的对比

  • 总结:
    • Oracle JDK版本将每三年发布一次,而OpenJDK版本每三个月发布一次;
    • OpenJDK 是一个参考模型并且是完全开源的,而Oracle JDK是OpenJDK的一个实现,并不是完全开源的;
    • Oracle JDK 比 OpenJDK 更稳定。OpenJDK和Oracle JDK的代码几乎相同,但Oracle JDK有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,我建议您选择Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用OpenJDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到Oracle JDK就可以解决问题;
    • 在响应性和JVM性能方面,Oracle JDK与OpenJDK相比提供了更好的性能;
    • Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本;
    • Oracle JDK根据二进制代码许可协议获得许可,而OpenJDK根据GPL v2许可获得许可。

19、什么是 Java 程序的主类? 应用程序和小程序的主类有何不同?

  • 一个程序中可以有多个类,但只能有一个类是主类。 主类是 Java 程序执行的入口点。
    • 在 Java 应用程序中,这个主类是指包含 main()方法的类,应用程序的主类不一定要求是 public 类。
    • 而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类,小程序的主类要求必须是 public 类。

20、Java 应用程序与小程序之间有那些差别?

  • 简单说应用程序是从主线程启动(也就是 main() 方法)。
  • applet 小程序没有main方法,主要是嵌在浏览器页面上运行(调用init()线程或者run()来启动),嵌入浏览器这点跟 flash 的小游戏类似。

22、字符型常量和字符串常量的区别?

  • 1、形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符
  • 2、含义上: 字符常量相当于一个整形值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  • 2、占内存大小 字符常量只占2个字节; 字符串常量占若干个字节(至少一个字符结束标志) (注意: char在Java中占两个字节)
    • 注意:java规范中,没有明确指出boolean的大小。在《Java虚拟机规范》给出了boolean类型单独使用是4个字节,和boolean数组是1个字节的定义,具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。https://blog.csdn.net/YuanMxy/article/details/74170745

21、构造器 Constructor 是否可被 override?

  • 构造方法并不能被继承,所以 Constructor 也就不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

22、自动装箱与拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;

  • 拆箱:将包装类型转换为基本数据类型;

23、在一个静态方法内调用一个非静态成员为什么是非法的?

  • 由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。

24、在 Java 中定义一个不做事且没有参数的构造方法的作用

  • Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。
    • 因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

25、import java和javax有什么区别?【都是Java 标准API的组成部分】

  • 刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。

所以,实际上java和javax没有区别。这都是一个名字

26、接口和抽象类的区别是什么?

1.抽象类可以有构造方法,接口中不能有构造方法。
2. 接口中的方法定义默认为 public abstract 类型,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
* 在Java8中,允许在接口中包含带有具体实现的方法,使用default修饰,这类方法就是默认方法。例如:

3. 接口中的成员变量类型默认为 public static final。,而抽象类中则不一定。
4.一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展多个接口。
5.抽象类中的抽象方法的访问权限可以是 public、protected 和默认类型(抽象方法就是为了被重写所以不能使用private关键字修饰!),接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型。
6. 抽象类中可以包含静态方法,在 JDK1.8 之前接口中不能包含静态方法,JDK1.8 以后可以包含。【之前不能包含是因为,接口不可以实现方法,只可以定义方法,所以不能使用静态方法(因为静态方法必须实现)】【现在可以包含了,直接用接口调用静态方法】
7. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
备注:在JDK8中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。(详见issue:https://github.com/Snailclimb/JavaGuide/issues/146)

27、成员变量与局部变量的区别有那些?

  • 1、**从语法形式上看:**成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,protected、static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
  • 2、**从变量在内存中的存储方式来看:**如果成员变量是使用static修饰的,那么这个成员变量是属于类的,如果没有使用static修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
  • 3、**从变量在内存中的生存时间上看:**成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
  • 4、成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外被 final 修饰的成员变量必须显示地赋值),而局部变量则不会自动赋值。

28、 创建一个对象用什么运算符?对象实体与对象引用有何不同?

  • new运算符,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向0个或1个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有n个引用指向它(可以用n条绳子系住一个气球)。

29、什么是方法的返回值?返回值在类的方法里的作用是什么?

方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!

30、 一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么?

主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法

31、构造方法有哪些特性?

  • 1、名字与类名相同。
  • 2、没有返回值,但不能用void声明构造函数。
  • 3、生成类的对象时自动执行,无需调用。

32、 静态方法和实例方法有何不同

  • 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

  • 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。

34、Java 中的异常处理

Java异常类层次结构图

  • 在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 Throwable类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。

  • Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(VirtualMachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。

    • 这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。
  • Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。

  • 注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

  • Throwable类常用方法

    • public string getMessage():返回异常发生时的详细信息
    • public string toString():返回异常发生时的简要描述
    • public string getLocalizedMessage():返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
    • public void printStackTrace():在控制台上打印Throwable对象封装的异常信息
  • 异常处理总结
    **try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
    **catch 块:**用于处理try捕获到的异常。
    **finally 块:**无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。

  • 在以下4种特殊情况下,finally块不会被执行:

    • 在finally语句块第一行发生了异常。 因为在其他行,finally块还是会得到执行
    • 在前面的代码中用了System.exit(int)已退出程序。 exit是带参函数 ;若该语句在异常语句之后,finally会执行
    • 程序所在的线程死亡。
    • 关闭CPU。
  • 关于返回值:

    • 如果try语句里有return,返回的是try语句块中变量值。 详细执行过程如下:

      • 如果有返回值,就把返回值保存到局部变量中;
      • 执行jsr指令跳到finally语句里执行;
      • 执行完finally语句后,返回之前保存在局部变量表里的值。
      • 如果try,finally语句里均有return,忽略try的return,而使用finally的return.

35、Java序列化中如果有些字段不想进行序列化,怎么办?

对于不想进行序列化的变量,使用transient关键字修饰。

transient关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。

36、获取用键盘输入常用的的两种方法

  • 方法1:通过 Scanner
    Scanner input = new Scanner(System.in);
    String s  = input.nextLine();
    input.close();

方法2:通过 BufferedReader

BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); 
String s = input.readLine(); 

37、常见关键字总结:static,final,this,super

final 关键字

  • final关键字主要用在三个地方:变量、方法、类。

  • 1、对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

  • 2、当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。

  • 使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final。

static 关键字

1)static 关键字主要有以下四种使用场景:

  • 修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:类名.静态变量名 类名.静态方法名()【方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。】
  • 静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.【如果静态代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问】
    • 静态代码块是自动执行的而静态方法是被调用的时候才执行的.
    • 非静态代码块与构造函数的区别是: 非静态代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块(非静态代码块)。也就是说,(非静态代码块)构造代码块中定义的是不同对象共性的初始化内容。
  • 静态内部类(static修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。
  • 静态导包(用来导入类中的静态资源,1.5之后的新特性): 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。【import static java.lang.Math.random;】

2)多线程访问static变量

先看一个类:

public class staticDemo {
    static int result;
    static int Addone(Integer num){
        Integer inner_result=num++;
        return inner_result;
    }
    public static void main(String[] args) {

    }
}

Addone方法会不会有多线程安全问题呢?没有!

  • 静态方法如果没有使用静态变量,则没有线程安全问题。
    为什么呢?因为静态方法内声明的变量是局部变量,它放在java栈中的,每个线程调用时,都会新创建一份,而不会共用一个存储单元。比如这里的inner_result,每个线程都会创建自己的一份,因此不会有线程安全问题。

  • 当多个线程执行同一个方法的时候,什么时候可能会出现异常结果?
    多个线程共享一块内存区域,在不加任何保护情况下对其操作就会出现异常结果,因为静态成员(static member)作为公共变量,就是放在共享内存区域的。
    比如上面那个例子,如果我在方法中用了静态变量result,就会导致问题

public class staticDemo {
    static int result;
    static int Addone(Integer num){
        result=num++;
        return result;
    }
    public static void main(String[] args) {

    }
}
  • 什么时候可能会得到正确的结果:

    • 不使用共享内存,每个线程内存空间相互独立;
    • 多线程共享一块内存区域,但是对这块共享区域加锁访问。对调用static变量的方法使用lock或synchronized

this 关键字

  • this关键字用于引用类的当前实例。 例如:
    class Manager {
        Employees[] employees;
         
        void manageEmployees() {
            int totalEmp = this.employees.length;
            System.out.println("Total employees: " + totalEmp);
            this.report();
        }
         
        void report() { }
    }

在上面的示例中,this关键字用于两个地方:

this.employees.length:访问类Manager的当前实例的变量。
this.report():调用类Manager的当前实例的方法。
此关键字是可选的,这意味着如果上面的示例在不使用此关键字的情况下表现相同。 但是,使用此关键字可能会使代码更易读或易懂。

super 关键字

  • super关键字用于从子类访问父类的变量和方法。 例如:
public class Super {
    protected int number;
     
    protected showNumber() {
        System.out.println("number = " + number);
    }
}
 
public class Sub extends Super {
    void bar() {
        super.number = 10;
        super.showNumber();
    }
}

在上面的例子中,Sub 类访问父类成员变量 number 并调用其其父类 Super 的 showNumber() 方法。

  • 使用 this 和 super 要注意的问题:

    • 在构造器中使用 super() 调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。另外,this 调用本类中的其他构造方法时,也要放在首行。
    • this、super不能用在static方法中:被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享。**而 this 代表对本类对象的引用,指向本类对象;而 super 代表对父类对象的引用,指向父类对象;**所以, this和super是属于对象范畴的东西,而静态方法是属于类范畴的东西。

38、Java 中 IO 流分为几种?BIO,NIO,AIO 有什么区别?【未弄懂】

https://github.com/zhaojing5340126/JavaGuide/blob/master/docs/java/Java基础知识.md#24-对象的相等与指向他们的引用相等两者有什么不同

猜你喜欢

转载自blog.csdn.net/ZHAOJING1234567/article/details/90055835