Java基础面试题(史上最全、持续更新、吐血推荐)


疯狂创客圈 史上最强 面试题 系列
Java基础
JVM面试题(史上最强、持续更新、吐血推荐) http://www.javashuo.com/article/p-vhnpdnhb-vd.html
Java基础面试题(史上最全、持续更新、吐血推荐) https://www.cnblogs.com/crazymakercircle/p/14366081.html
死锁面试题(史上最强、持续更新) http://www.javashuo.com/article/p-uyudvdol-vd.html
多线程、线程池、内置锁 面试题 (史上最强、持续更新) http://www.javashuo.com/article/p-tecubweb-vd.html
JUC并发包与容器类 - 面试题(史上最强、持续更新) http://www.javashuo.com/article/p-xcyjkbgi-vd.html
SpringBoot - 面试题(史上最强、持续更新) http://www.javashuo.com/article/p-xjwdvxjl-vd.html
Linux面试题(史上最全、持续更新、吐血推荐) http://www.javashuo.com/article/p-blmdtuaw-vd.html
分布式、高并发、设计模式、架构
Zookeeper 面试题(史上最强、持续更新) http://www.javashuo.com/article/p-fvxlillp-vd.html
Mysql 面试题(史上最强、持续更新) http://www.javashuo.com/article/p-xhmrgfwk-vd.html
Redis 面试题 - 收藏版(史上最强、持续更新) http://www.javashuo.com/article/p-vkywkhov-vd.html
SpringCloud 面试题 - 收藏版(史上最强、持续更新) http://www.javashuo.com/article/p-pptrkvmu-vd.html
Netty 面试题 (史上最强、持续更新) http://www.javashuo.com/article/p-guuqkocg-vd.html
消息队列、RabbitMQ、Kafka、RocketMQ面试题 (史上最全、持续更新、吐血推荐) http://www.javashuo.com/article/p-hiiczuxl-vd.html
设计模式面试题 (史上最全、持续更新、吐血推荐) http://www.javashuo.com/article/p-qnkzhtsu-vd.html

Java概述

何为编程

编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终获得结果的过程。html

为了使计算机可以理解人的意图,人类就必需要将需解决的问题的思路、方法、和手段经过计算机可以理解的形式告诉计算机,使得计算机可以根据人的指令一步一步去工做,完成某种特定的任务。这种人和计算机之间交流的过程就是编程。java

什么是Java

Java是一门面向对象编程语言,不只吸取了C++语言的各类优势,还摒弃了C++里难以理解的多继承、指针等概念,所以Java语言具备功能强大和简单易用两个特征。Java语言做为静态面向对象编程语言的表明,极好地实现了面向对象理论,容许程序员以优雅的思惟方式进行复杂的编程 。程序员

jdk1.5以后的三大版本

  • Java SE(J2SE,Java 2 Platform Standard Edition,标准版)
    Java SE 之前称为 J2SE。它容许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为Java EE和Java ME提供基础。
  • Java EE(J2EE,Java 2 Platform Enterprise Edition,企业版)
    Java EE 之前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端Java 应用程序。Java EE 是在 Java SE 的基础上构建的,它提供 Web 服务、组件模型、管理和通讯 API,能够用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web2.0应用程序。2018年2月,Eclipse 宣布正式将 JavaEE 改名为 JakartaEE
  • Java ME(J2ME,Java 2 Platform Micro Edition,微型版)
    Java ME 之前称为 J2ME。Java ME 为在移动设备和嵌入式设备(好比手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对能够动态下载的连网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次,就能够用于许多设备,并且能够利用每一个设备的本机功能。

JVM、JRE和JDK的关系

JVM
Java Virtual Machine是Java虚拟机,Java程序须要运行在虚拟机上,不一样的平台有本身的虚拟机,所以Java语言能够实现跨平台。web

JRE
Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包面试

若是想要运行一个开发好的Java程序,计算机中只须要安装JRE便可。算法

JDK
Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。因此安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等sql

JVM&JRE&JDK关系图数据库

img

什么是跨平台性?原理是什么

所谓跨平台性,是指java语言编写的程序,一次编译后,能够在多个系统平台上运行。编程

实现原理:Java程序是经过java虚拟机在系统平台上运行的,只要该系统能够安装相应的java虚拟机,该系统就能够运行java程序。小程序

Java语言有哪些特色

简单易学(Java语言的语法与C语言和C++语言很接近)

面向对象(封装,继承,多态)

平台无关性(Java虚拟机实现平台无关性)

支持网络编程而且很方便(Java语言诞生自己就是为简化网络编程设计的)

支持多线程(多线程机制使应用程序在同一时间并行执行多项任)

健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等)

安全性

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

字节码:Java源代码通过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。

采用字节码的好处

Java语言经过字节码的方式,在必定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特色。因此Java程序运行时比较高效,并且,因为字节码并不专对一种特定的机器,所以,Java程序无须从新编译即可在多种不一样的计算机上运行。

先看下java中的编译器和解释器

Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只须要面向虚拟机,生成虚拟机可以理解的代码,而后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫作字节码(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不一样的,可是实现的虚拟机是相同的。Java源程序通过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,而后在特定的机器上运行,这就是上面提到的Java的特色的编译与解释并存的解释。

Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。
1

什么是Java程序的主类?应用程序和小程序的主类有何不一样?

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

Java应用程序与小程序之间有那些差异?

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

Java和C++的区别

我知道不少人没学过C++,可是面试官就是没事喜欢拿我们Java和C++比呀!没办法!!!就算没学过C++,也要记下来!

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

Oracle JDK 和 OpenJDK 的对比

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

面向对象

什么是面向对象?

面向对象程序设计是以创建模型体现出来的抽象思惟过程和面向对象的方法。咱们能够将某个事物抽象出来,赋予它本身的特征,而且能够针对这个事物进行相应的操做,以及规定与其余对象之间的关系。能够下降代码的耦合度,使程序更加灵活。

多态的好处

容许不一样类对象对同一消息作出响应,即同一消息能够根据发送对象的不一样而采用多种不一样的行为方式(发送消息就是函数调用)。即父类型的引用指向子类型的对象。主要有如下优势:

可替换性:多态对已存在代码具备可替换性

可扩充性:增长新的子类不影响已经存在的类结构

更加灵活

面向对象和面向过程的区别?

面向过程

优势:性能比面向对象高,由于类调用时须要实例化,开销比较大,比较消耗资源;好比单片机、嵌入式开发、Linux/Unix等通常采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展

面向对象

优势:易维护、易复用、易扩展,因为面向对象有封装、继承、多态性的特性,能够设计出低耦合的系统,使系统更加灵活、更加易于维护

缺点:性能比面向过程低

面向过程是具体化的,流程化的,解决一个问题,你须要一步一步的分析,一步一步的实现。

面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。须要什么功能直接使用就能够了,没必要去一步一步的实现,至于这个功能是如何实现的,管咱们什么事?咱们会用就能够了。

面向对象的底层其实仍是面向过程,把面向过程抽象成类,而后封装,方便咱们使用的就是面向对象了。

抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

面向对象三大特性?

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

封装

封装把一个对象的属性私有化,同时提供一些能够被外界访问的属性的方法,若是属性不想被外界访问,咱们大可没必要提供方法给外界访问。可是若是一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

封装隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提升复用性和安全性。

继承

继承是使用已存在的类的定义做为基础创建新类的技术,新类的定义能够增长新的数据或新的功能,也能够用父类的功能,但不能选择性地继承父类。经过使用继承咱们可以很是方便地复用之前的代码。

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

  1. 子类拥有父类非 private 的属性和方法。
  2. 子类能够拥有本身属性和方法,即子类能够对父类进行扩展。
  3. 子类能够用本身的方式实现父类的方法。(之后介绍)。
多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和经过该引用变量发出的方法调用在编程时并不肯定,而是在程序运行期间才肯定,即一个引用变量到底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。

在Java中有两种形式能够实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

在Java中有两种形式能够实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。

一个引用变量到底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。运行时的多态是面向对象最精髓的东西,要实现多态须要作两件事:

  • 方法重写(子类继承父类并重写父类中已有的或抽象的方法);

  • 对象造型(用父类型引用子类型对象,这样一样的引用调用一样的方法就会根据子类对象的不一样而表现出不一样的行为)。

什么是多态机制?Java语言是如何实现多态的?

所谓多态就是指程序中定义的引用变量所指向的具体类型和经过该引用变量发出的方法调用在编程时并不肯定,而是在程序运行期间才肯定,即一个引用变量倒底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。由于在程序运行时才肯定具体的类,这样,不用修改源程序代码,就可让引用变量绑定到各类不一样的类实现上,从而致使该引用调用的具体方法随之改变,即不修改程序代码就能够改变程序运行时所绑定的具体代码,让程序能够选择多个运行状态,这就是多态性。

多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不一样来区分不一样的函数,经过编辑以后会变成两个不一样的函数,在运行时谈不上多态。而运行时多态是动态的,它是经过动态绑定来实现的,也就是咱们所说的多态性。

多态的实现

Java实现多态有三个必要条件:继承、重写、向上转型。

继承:在多态中必须存在有继承关系的子类和父类。

重写:子类对父类中某些方法进行从新定义,在调用这些方法时就会调用子类的方法。

向上转型:在多态中须要将子类的引用赋给父类对象,只有这样该引用才可以具有技能调用父类的方法和子类的方法。

只有知足了上述三个条件,咱们才可以在同一个继承结构中使用统一的逻辑实现代码处理不一样的对象,从而达到执行不一样的行为。

对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,可是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

面向对象五大基本原则是什么?

  • 单一职责原则SRP(Single Responsibility Principle)
    类的功能要单一,不能一应俱全,跟杂货铺似的。

  • 开放封闭原则OCP(Open-Close Principle)
    一个模块对于拓展是开放的,对于修改是封闭的,想要增长功能热烈欢迎,想要修改,哼,一万个不乐意。

  • 里式替换原则LSP(the Liskov Substitution Principle LSP)
    子类能够替换父类出如今父类可以出现的任何地方。好比你能表明你爸去你姥姥家干活。哈哈~~

  • 依赖倒置原则DIP(the Dependency Inversion Principle DIP)
    高层次的模块不该该依赖于低层次的模块,他们都应该依赖于抽象。抽象不该该依赖于具体实现,具体实现应该依赖于抽象。就是你出国要说你是中国人,而不能说你是哪一个村子的。好比说中国人是抽象的,下面有具体的xx省,xx市,xx县。你要依赖的抽象是中国人,而不是你是xx村的。

  • 接口分离原则ISP(the Interface Segregation Principle ISP)
    设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。就好比一个手机拥有打电话,看视频,玩游戏等功能,把这几个功能拆分红不一样的接口,比在一个接口里要好的多。

什么是值传递和引用传递?

值传递,是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。

引用传递,通常是对于对象型变量而言的,传递的是该对象地址的一个副本,并非原对象自己。

通常认为,Java 内的传递都是值传递,Java 中实例对象的传递是引用传递。

代码中如何实现多态

实现多态主要有如下三种方式:

  1. 接口实现

  2. 继承父类重写方法

  3. 同一类中进行方法重载

接口的意义

规范,扩展,回调。

抽象类的意义

为其余子类提供一个公共的类型

封装子类中重复定义的内容

定义抽象方法,子类虽然有不一样的实现,可是定义时一致的

接口和抽象类的区别

比较 抽象类 接口
默认方法 抽象类能够有默认的方法实现 java 8以前,接口中不存在方法的实现.
实现方式 子类使用extends关键字来继承抽象类.若是子类不是抽象类,子类须要提供抽象类中所声明方法的实现 子类使用implements来实现接口,须要提供接口中全部声明的实现
构造器 抽象类中能够有构造器, 接口中不能
访问修饰符 抽象方法能够有public,protected和default等修饰 接口默认是public,不能使用其余修饰符
多继承 一个子类只能存在一个父类 一个子类能够存在多个接口
访问新方法 想抽象类中添加新方法,能够提供默认的实现,所以能够不修改子类现有的代码 若是往接口中添加新方法,则子类中须要实现该方法.

父类的静态方法可否被子类重写

不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,咱们通常称之为隐藏。

什么是不可变对象

不可变对象指对象一旦被建立,状态就不能再改变。任何修改都会建立一个新的对象,如 String、Integer及其它包装类。

静态变量和实例变量的区别?

静态变量存储在方法区,属于类全部。实例变量存储在堆当中,其引用存在当前线程栈。

可否建立一个包含可变对象的不可变对象?

固然能够建立一个包含可变对象的不可变对象的,你只须要谨慎一点,不要共享可变对象的引用就能够了,若是须要变化时,就返回原对象的一个拷贝。最多见的例子就是对象中包含一个日期对象的引用。

Overload和Override的区别。Overloaded的方法是否能够改变返回值的类型

答: 方法的重写Overriding和重载Overloading是Java多态性的不一样表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。若是在子类中定义某方法与其父类有相同的名称和参数,咱们说该方法被重写 (Overriding)。若是在一个类中定义了多个同名的方法,它们或有不一样的参数个数或有不一样的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是能够改变返回值的类型

基础语法

数据类型

Java有哪些数据类型

定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不一样大小的内存空间。

分类

  • 基本数据类型
    • 数值型
      • 整数类型(byte,short,int,long)
      • 浮点类型(float,double)
    • 字符型(char)
    • 布尔型(boolean)
  • 引用数据类型
    • 类(class)
    • 接口(interface)
    • 数组([])

Java基本数据类型图

img

switch 是否能做用在 byte 上,是否能做用在 long 上,是否能做用在 String 上

在 Java 5 之前,switch(expr)中,expr 只能是 byte、short、char、int。从 Java5 开始,Java 中引入了枚举类型,expr 也能够是 enum 类型,从 Java 7 开始,expr 还能够是字符串(String),可是长整型(long)在目前全部的版本中都是不能够的。

用最有效率的方法计算 2 乘以 8

2 << 3(左移 3 位至关于乘以 2 的 3 次方,右移 3 位至关于除以 2 的 3 次方)。

Math.round(11.5) 等于多少?Math.round(-11.5)等于多少

Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加 0.5 而后进行下取整。

float f=3.4;是否正确

不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会形成精度损失,所以须要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F;。

short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗

对于 short s1 = 1; s1 = s1 + 1;因为 1 是 int 类型,所以 s1+1 运算结果也是 int型,须要强制转换类型才能赋值给 short 型。

而 short s1 = 1; s1 += 1;能够正确编译,由于 s1+= 1;至关于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。

编码

Java语言采用何种编码方案?有何特色?

Java语言采用Unicode编码标准,Unicode(标准码),它为每一个字符制订了一个惟一的数值,所以在任何的语言,平台,程序均可以放心的使用。

注释

什么Java注释

定义:用于解释说明程序的文字

分类

  • 单行注释
    格式: // 注释文字
  • 多行注释
    格式: /* 注释文字 */
  • 文档注释
    格式:/** 注释文字 */

做用

在程序中,尤为是复杂的程序中,适当地加入注释能够增长程序的可读性,有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。

注意事项:多行和文档注释都不能嵌套使用。

访问修饰符

访问修饰符 public,private,protected,以及不写(默认)时的区别

定义:Java中,能够使用访问修饰符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不一样的访问权限。

分类

private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
protected : 对同一包内的类和全部子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
public : 对全部类可见。使用对象:类、接口、变量、方法

访问修饰符图

img

运算符

&和&&的区别

&运算符有两种用法:(1)按位与;(2)逻辑与。

&&运算符是短路与运算。逻辑与跟短路与的差异是很是巨大的,虽然两者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之因此称为短路运算,是由于若是&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。

注意:逻辑或运算符(|)和短路或运算符(||)的差异也是如此。

3*0.1==0.3返回值是什么

false,由于有些浮点数不能彻底精确的表示出来。

a=a+b与a+=b有什么区别吗?

+=操做符会进行隐式自动类型转换,此处a+=b隐式的将加操做的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换。如:

byte a = 127;

byte b = 127;

b = a + b; // error : cannot convert from int to byte

b += a; // ok

(译者注:这个地方应该表述的有误,其实不管 a+b 的值为多少,编译器都会报错,由于 a+b 操做会将 a、b 提高为 int 类型,因此将 int 类型赋值给 byte 就会编译出错)

short s1= 1; s1 = s1 + 1; 该段代码是否有错,有的话怎么改?

有错误,short类型在进行运算时会自动提高为int类型,也就是说s1+1的运算结果是int类型。

int 和 Integer 有什么区别

答: Java 提供两种不一样的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每一个原始类型提供了封装类。引用类型和原始类型具备不一样的特征和用法,它们包括:大小和速度问题,这种类型以哪一种类型的数据结构存储,当引用类型和原始类型用做某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关

& 和 &&的区别

&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差异是很是巨大的,虽然两者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之因此称为短路运算是由于,若是&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。不少时候咱们可能都须要用&&而不是&,例如在验证用户登陆时断定用户名不是null并且不是空字符串,应当写为:username != null &&!username.equals(“”),两者的顺序不能交换,更不能用&运算符,由于第一个条件若是不成立,根本不能进行字符串的equals比较,不然会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差异也是如此。

如何将byte转为String

能够使用 String 接收 byte[] 参数的构造器来进行转换,须要注意的点是要使用的正确的编码,不然会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不一样。

能够将int强转为byte类型么?会产生什么问题?

咱们能够作强制转换,可是Java中int是32位的而byte是8 位的,因此,若是强制转化int类型的高24位将会被丢弃,byte 类型的范围是从-128到128

关键字

Java 有没有 goto

goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。

final 有什么用?

用于修饰类、属性和方法;

  • 被final修饰的类不能够被继承
  • 被final修饰的方法不能够被重写
  • 被final修饰的变量不能够被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是能够改变的

final有哪些用法

final也是不少面试喜欢问的地方,能回答下如下三点就不错了:

1.被final修饰的类不能够被继承

2.被final修饰的方法不能够被重写

3.被final修饰的变量不能够被改变。若是修饰引用,那么表示引用不可变,引用指向的内容可变。

4.被final修饰的方法,JVM会尝试将其内联,以提升运行效率

5.被final修饰的常量,在编译阶段会存入常量池中。

回答出编译器对final域要遵照的两个重排序规则更好:

1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操做之间不能重排序。

2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操做之间不能重排序。

final finally finalize区别

  • final能够修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表
    示该变量是一个常量不能被从新赋值。
  • finally通常做用在try-catch代码块中,在处理异常的时候,一般咱们将必定要执行的代码方法finally代码块
    中,表示无论是否出现异常,该代码块都会执行,通常用来存放一些关闭资源的代码。
  • finalize是一个方法,属于Object类的一个方法,而Object类是全部类的父类,该方法通常由垃圾回收器来调
    用,当咱们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的
    最后判断。

this关键字的用法

this是自身的一个对象,表明对象自己,能够理解为:指向对象自己的一个指针。

this的用法在java中大致能够分为3种:

1.普通的直接引用,this至关因而指向当前对象自己。

2.形参与成员名字重名,用this来区分:

public Person(String name, int age) {
    this.name = name;
    this.age = age;
}
1234

3.引用本类的构造函数

class Person{
    private String name;
    private int age;
    
    public Person() {
    }
 
    public Person(String name) {
        this.name = name;
    }
    public Person(String name, int age) {
        this(name);
        this.age = age;
    }
}
123456789101112131415

super关键字的用法

super能够理解为是指向本身超(父)类对象的一个指针,而这个超类指的是离本身最近的一个父类。

super也有三种用法:

1.普通的直接引用

与this相似,super至关因而指向当前对象的父类的引用,这样就能够用super.xxx来引用父类的成员。

2.子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分

class Person{
    protected String name;
 
    public Person(String name) {
        this.name = name;
    }
 
}
 
class Student extends Person{
    private String name;
 
    public Student(String name, String name1) {
        super(name);
        this.name = name1;
    }
 
    public void getInfo(){
        System.out.println(this.name);      //Child
        System.out.println(super.name);     //Father
    }
 
}

public class Test {
    public static void main(String[] args) {
       Student s1 = new Student("Father","Child");
       s1.getInfo();
 
    }
}
12345678910111213141516171819202122232425262728293031

3.引用父类构造函数

三、引用父类构造函数

  • super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
  • this(参数):调用本类中另外一种形式的构造函数(应该为构造函数中的第一条语句)。

this与super的区别

  • super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
  • this:它表明当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;若是函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
  • super()和this()相似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。
  • super()和this()均需放在构造方法内第一行。
  • 尽管能够用this调用一个构造器,但却不能调用两个。
  • this和super不能同时出如今一个构造函数里面,由于this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,因此在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会经过。
  • this()和super()都指的是对象,因此,均不能够在static环境中使用。包括:static变量,static方法,static语句块。
  • 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。

static存在的主要意义

static的主要意义是在于建立独立于具体对象的域变量或者方法。以至于即便没有建立对象,也能使用属性和调用方法

static关键字还有一个比较关键的做用就是 用来造成静态代码块以优化程序性能。static块能够置于类中的任何地方,类中能够有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每一个static块,而且只会执行一次。

为何说static块能够用来优化程序性能,是由于它的特性:只会在类加载的时候执行一次。所以,不少时候会将一些只须要进行一次的初始化操做都放在static代码块中进行。

static的独特之处

一、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,咱们都知道一个类能够建立多个实例!】,全部的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我以为我已经讲的很通俗了,你明白了咩?

二、在该类被第一次加载的时候,就会去加载被static修饰的部分,并且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据须要是能够再次赋值的。

三、static变量值在类加载的时候分配空间,之后建立类对象的时候不会从新分配。赋值的话,是能够任意赋值的!

四、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕以后,即使没有建立对象,也能够去访问。

static应用场景

由于static是被类的实例对象所共享,所以若是某个成员变量是被全部对象所共享的,那么这个成员变量就应该定义为静态变量

所以比较常见的static应用场景有:

一、修饰成员变量 二、修饰成员方法 三、静态代码块 四、修饰类【只能修饰内部类也就是静态内部类】 五、静态导包

static注意事项

一、静态只能访问静态。 二、非静态既能够访问非静态的,也能够访问静态的。

流程控制语句

break ,continue ,return 的区别及做用

break 跳出总上一层循环,再也不执行循环(结束当前的循环体)

continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)

return 程序返回,再也不执行下面的代码(结束当前的方法 直接返回)

在 Java 中,如何跳出当前的多重嵌套循环

在Java中,要想跳出多重循环,能够在外面的循环语句前定义一个标号,而后在里层循环体的代码中使用带有标号的break 语句,便可跳出外层循环。例如:

public static void main(String[] args) {
    ok:
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            System.out.println("i=" + i + ",j=" + j);
            if (j == 5) {
                break ok;
            }

        }
    }
}
123456789101112

类与接口

抽象类和接口的对比

抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。

从设计层面来讲,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

相同点

  • 接口和抽象类都不能实例化
  • 都位于继承的顶端,用于被其余实现或继承
  • 都包含抽象方法,其子类都必须覆写这些抽象方法

不一样点

参数 抽象类 接口
声明 抽象类使用abstract关键字声明 接口使用interface关键字声明
实现 子类使用extends关键字来继承抽象类。若是子类不是抽象类的话,它须要提供抽象类中全部声明的方法的实现 子类使用implements关键字来实现接口。它须要提供接口中全部声明的方法的实现
构造器 抽象类能够有构造器 接口不能有构造器
访问修饰符 抽象类中的方法能够是任意访问修饰符 接口方法默认修饰符是public。而且不容许定义为 private 或者 protected
多继承 一个类最多只能继承一个抽象类 一个类能够实现多个接口
字段声明 抽象类的字段声明能够是任意的 接口的字段默认都是 static 和 final 的

备注:Java8中接口中引入默认方法和静态方法,以此来减小抽象类和接口之间的差别。

如今,咱们能够为接口提供默认实现的方法了,而且不用强制子类来实现它。

接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵照这样一个原则:

  • 行为模型应该老是经过接口而不是抽象类定义,因此一般是优先选用接口,尽可能少用抽象类。
  • 选择抽象类的时候一般是以下状况:须要定义子类的行为,又要为子类提供通用的功能。

普通类和抽象类有哪些区别?

  • 普通类不能包含抽象方法,抽象类能够包含抽象方法。
  • 抽象类不能直接实例化,普通类能够直接实例化。

抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其余类继承的,若是定义为 final 该类就不能被继承,这样彼此就会产生矛盾,因此 final 不能修饰抽象类

建立一个对象用什么关键字?对象实例与对象引用有何不一样?

new关键字,new建立对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用能够指向0个或1个对象(一根绳子能够不系气球,也能够系一个气球);一个对象能够有n个引用指向它(能够用n条绳子系住一个气球)

Java中,什么是构造方法?什么是构造方法重载?什么是复制构造方法?

当新对象被建立的时候,构造方法会被调用。每个类都有构造方法。在程序员没有给类提供构造方法的状况下,Java编译器会为这个类建立一个默认的构造方法。

Java中构造方法重载和方法重载很类似。能够为一个类建立多个构造方法。每个构造方法必须有它本身惟一的参数列表。

Java不支持像C++中那样的复制构造方法,这个不一样点是由于若是你不本身写构造方法的状况下,Java不会建立默认的复制构造方法。

Java支持多继承么?

Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 可是java中的接口支持多继承,,即一个子接口能够有多个父接口。(接口的做用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。

变量与方法

成员变量与局部变量的区别有哪些

变量:在程序执行的过程当中,在某个范围内其值能够发生改变的量。从本质上讲,变量实际上是内存中的一小块区域

成员变量:方法外部,类内部定义的变量

局部变量:类的方法中的变量。

成员变量和局部变量的区别

做用域

成员变量:针对整个类有效。
局部变量:只在某个范围内有效。(通常指的就是方法,语句体内)

存储位置

成员变量:随着对象的建立而存在,随着对象的消失而消失,存储在堆内存中。
局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。

生命周期

成员变量:随着对象的建立而存在,随着对象的消失而消失
局部变量:当方法调用完,或者语句结束后,就自动释放。

初始值

成员变量:有默认初始值。

局部变量:没有默认初始值,使用前必须赋值。

使用原则

在使用变量时须要遵循的原则为:就近原则
首先在局部范围找,有就使用;接着在成员位置找。

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

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

在调用子类构造方法以前会先调用父类没有参数的构造方法,其目的是?

帮助子类作初始化工做。

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

主要做用是完成对类对象的初始化工做。能够执行。由于一个类即便没有声明构造方法也会有默认的不带参数的构造方法。

构造方法有哪些特性?

名字与类名相同;

没有返回值,但不能用void声明构造函数;

生成类的对象时自动执行,无需调用。

静态变量和实例变量区别

静态变量: 静态变量因为不属于任何实例对象,属于类的,因此在内存中只会有一份,在类的加载过程当中,JVM只为静态变量分配一次内存空间。

实例变量: 每次建立对象,都会为每一个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,建立几回对象,就有几份成员变量。

静态变量与普通变量区别

static变量也称做静态变量,静态变量和非静态变量的区别是:静态变量被全部的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在建立对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。

静态方法和实例方法有何不一样?

静态方法和实例方法的区别主要体如今两个方面:

  1. 在外部调用静态方法时,能够使用"类名.方法名"的方式,也能够使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法能够无需建立对象。
  2. 静态方法在访问本类的成员时,只容许访问静态成员(即静态成员变量和静态方法),而不容许访问实例成员变量和实例方法;实例方法则无此限制

在一个静态方法内调用一个非静态成员为何是非法的?

因为静态方法能够不经过对象进行调用,所以在静态方法里,不能调用其余非静态变量,也不能够访问非静态变量成员。

什么是方法的返回值?返回值的做用是什么?

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

Object根类

equals()和hashcode()的联系

hashCode()是Object类的一个方法,返回一个哈希值。若是两个对象根据equal()方法比较相等,那么调用这两个对象中任意一个对象的hashCode()方法必须产生相同的哈希值。

若是两个对象根据eqaul()方法比较不相等,那么产生的哈希值不必定相等(碰撞的状况下仍是会相等的。)

a.hashCode()有什么用?

将对象放入到集合中时,首先判断要放入对象的hashcode是否已经在集合中存在,不存在则直接放入集合。若是hashcode相等,而后经过equal()方法判断要放入对象与集合中的任意对象是否相等:若是equal()判断不相等,直接将该元素放入集合中,不然不放入。

有没有可能两个不相等的对象有相同的hashcode

有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为何在 hashmap 中会有冲突。若是两个对象相等,必须有相同的hashcode 值,反之不成立。

能够在hashcode中使用随机数字吗?

不行,由于同一对象的 hashcode 值必须是相同的

a==b与a.equals(b)有什么区别

若是a 和b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,因此一般须要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,因此能够用于两个不一样对象,可是包含的字母相同的比较。

基本类型比较用,比较的是他们的值。默认下,对象用比较时,比较的是内存地址,若是须要比较对象内容,须要重写equal方法。

为何要转换?

若是你在 Java5 下进行过编程的话,你必定不会陌生这一点,你不能直接地向集合( Collection )中放入原始类型值,由于集合只接收对象。

一般这种状况下你的作法是,将这些原始类型的值转换成对象,而后将这些转换的对象放入集合中。使用 Integer、Double、Boolean 等这些类,咱们能够将原始类型值转换成对应的对象,可是从某些程度可能使得代码不是那么简洁精炼。

为了让代码简练,Java5 引入了具备在原始类型和对象类型自动转换的装箱和拆箱机制。

可是自动装箱和拆箱并不是完美,在使用时须要有一些注意事项,若是没有搞明白自动装箱和拆箱,可能会引发难以察觉的 Bug 。

内部类

什么是内部类?

在Java中,能够将一个类的定义放在另一个类的定义内部,这就是内部类。内部类自己就是类的一个属性,与其余属性定义方式一致。

内部类的分类有哪些

内部类能够分为四种:成员内部类、局部内部类、匿名内部类和静态内部类

静态内部类

定义在类内部的静态类,就是静态内部类。

public class Outer {

    private static int radius = 1;

    static class StaticInner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
        }
    }
}
12345678910

静态内部类能够访问外部类全部的静态变量,而不可访问外部类的非静态变量;静态内部类的建立方式,new 外部类.静态内部类(),以下:

Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();
12
成员内部类

定义在类内部,成员位置上的非静态类,就是成员内部类。

public class Outer {

    private static  int radius = 1;
    private int count =2;
    
     class Inner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
            System.out.println("visit outer   variable:" + count);
        }
    }
}
123456789101112

成员内部类能够访问外部类全部的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的建立方式外部类实例.new 内部类(),以下:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();
123
局部内部类

定义在方法中的内部类,就是局部内部类。

public class Outer {

    private  int out_a = 1;
    private static int STATIC_b = 2;

    public void testFunctionClass(){
        int inner_c =3;
        class Inner {
            private void fun(){
                System.out.println(out_a);
                System.out.println(STATIC_b);
                System.out.println(inner_c);
            }
        }
        Inner  inner = new Inner();
        inner.fun();
    }
    public static void testStaticFunctionClass(){
        int d =3;
        class Inner {
            private void fun(){
                // System.out.println(out_a); 编译错误,定义在静态方法中的局部类不能够访问外部类的实例变量
                System.out.println(STATIC_b);
                System.out.println(d);
            }
        }
        Inner  inner = new Inner();
        inner.fun();
    }
}
123456789101112131415161718192021222324252627282930

定义在实例方法中的局部类能够访问外部类的全部变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的建立方式,在对应方法内,new 内部类(),以下:

public static void testStaticFunctionClass(){
    class Inner {
    }
    Inner  inner = new Inner();
 }
12345
匿名内部类

匿名内部类就是没有名字的内部类,平常开发中使用的比较多。

public class Outer {

    private void test(final int i) {
        new Service() {
            public void method() {
                for (int j = 0; j < i; j++) {
                    System.out.println("匿名内部类" );
                }
            }
        }.method();
    }
 }
 //匿名内部类必须继承或实现一个已有的接口 
 interface Service{
    void method();
}
12345678910111213141516

除了没有名字,匿名内部类还有如下特色:

  • 匿名内部类必须继承一个抽象类或者实现一个接口。
  • 匿名内部类不能定义任何静态成员和静态方法。
  • 当所在的方法的形参须要被匿名内部类使用时,必须声明为 final。
  • 匿名内部类不能是抽象的,它必需要实现继承的类或者实现的接口的全部抽象方法。

匿名内部类建立方式:

new 类/接口{ 
  //匿名内部类实现部分
}
123

内部类的优势

咱们为何要使用内部类呢?由于它有如下优势:

  • 一个内部类对象能够访问建立它的外部类对象的内容,包括私有数据!
  • 内部类不为同一包的其余类所见,具备很好的封装性;
  • 内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
  • 匿名内部类能够很方便的定义回调。

内部类有哪些应用场景

  1. 一些多算法场合
  2. 解决一些非面向对象的语句块。
  3. 适当使用内部类,使得代码更加灵活和富有扩展性。
  4. 当某个类除了它的外部类,再也不被其余的类使用时。

局部内部类和匿名内部类访问局部变量的时候,为何变量必需要加上final?

局部内部类和匿名内部类访问局部变量的时候,为何变量必需要加上final呢?它内部原理是什么呢?

先看这段代码:

public class Outer {

    void outMethod(){
        final int a =10;
        class Inner {
            void innerMethod(){
                System.out.println(a);
            }

        }
    }
}
123456789101112

以上例子,为何要加final呢?是由于生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,若是局部内部类要调用局部变量时,就会出错。加了final,能够确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。

内部类相关,看程序说出运行结果

public class Outer {
    private int age = 12;

    class Inner {
        private int age = 13;
        public void print() {
            int age = 14;
            System.out.println("局部变量:" + age);
            System.out.println("内部类变量:" + this.age);
            System.out.println("外部类变量:" + Outer.this.age);
        }
    }

    public static void main(String[] args) {
        Outer.Inner in = new Outer().new Inner();
        in.print();
    }

}
12345678910111213141516171819

运行结果:

局部变量:14
内部类变量:13
外部类变量:12
123

重写与重载

构造器(constructor)是否可被重写(override)

构造器不能被继承,所以不能被重写,但能够被重载。

重载(Overload)和重写(Override)的区别。重载的方法可否根据返回类型进行区分?

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,然后者实现的是运行时的多态性。

重载:发生在同一个类中,方法名相同参数列表不一样(参数类型不一样、个数不一样、顺序不一样),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);若是父类方法访问修饰符为private则子类中就不是重写。

对象相等判断

== 和 equals 的区别是什么

== : 它的做用是判断两个对象的地址是否是相等。即,判断两个对象是否是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

equals() : 它的做用也是判断两个对象是否相等。但它通常有两种使用状况:

状况1:类没有覆盖 equals() 方法。则经过 equals() 比较该类的两个对象时,等价于经过“==”比较这两个对象。

状况2:类覆盖了 equals() 方法。通常,咱们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

举个例子:

public class test1 {
    public static void main(String[] args) {
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b为另外一个引用,对象的内容同样
        String aa = "ab"; // 放在常量池中
        String bb = "ab"; // 从常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}
1234567891011121314151617

说明:

  • String中的equals方法是被重写过的,由于object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
  • 当建立String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要建立的值相同的对象,若是有就把它赋给当前引用。若是没有就在常量池中从新建立一个String对象。

hashCode 与 equals (重要)

HashSet如何检查重复

两个对象的 hashCode() 相同,则 equals() 也必定为 true,对吗?

hashCode和equals方法的关系

面试官可能会问你:“你重写过 hashcode 和 equals 么,为何重写equals时必须重写hashCode方法?”

hashCode()介绍

hashCode() 的做用是获取哈希码,也称为散列码;它其实是返回一个int整数。这个哈希码的做用是肯定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

散列表存储的是键值对(key-value),它的特色是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(能够快速找到所须要的对象)

为何要有 hashCode

咱们以“HashSet 如何检查重复”为例子来讲明为何要有 hashCode

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其余已经加入的对象的 hashcode 值做比较,若是没有相符的hashcode,HashSet会假设对象没有重复出现。可是若是发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。若是二者相同,HashSet 就不会让其加入操做成功。若是不一样的话,就会从新散列到其余位置。(摘自个人Java启蒙书《Head first java》第二版)。这样咱们就大大减小了 equals 的次数,相应就大大提升了执行速度。

hashCode()与equals()的相关规定

若是两个对象相等,则hashcode必定也是相同的

两个对象相等,对两个对象分别调用equals方法都返回true

两个对象有相同的hashcode值,它们也不必定是相等的

所以,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

hashCode() 的默认行为是对堆上的对象产生独特值。若是没有重写 hashCode(),则该 class 的两个对象不管如何都不会相等(即便这两个对象指向相同的数据)

对象的相等与指向他们的引用相等,二者有什么不一样?

对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。

值传递

当一个对象被看成参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里究竟是值传递仍是引用传递

是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例做为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性能够在被调用过程当中被改变,但对对象引用的改变是不会影响到调用者的

为何 Java 中只有值传递

首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法能够修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各类程序设计语言(不仅是Java)中方法参数传递方式。

Java程序设计语言老是采用按值调用。也就是说,方法获得的是全部参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

下面经过 3 个例子来给你们说明

example 1

public static void main(String[] args) {
    int num1 = 10;
    int num2 = 20;

    swap(num1, num2);

    System.out.println("num1 = " + num1);
    System.out.println("num2 = " + num2);
}

public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;

    System.out.println("a = " + a);
    System.out.println("b = " + b);
}
123456789101112131415161718

结果

a = 20
b = 10
num1 = 10
num2 = 20
1234

解析

img

在swap方法中,a、b的值进行交换,并不会影响到 num一、num2。由于,a、b中的值,只是从 num一、num2 的复制过来的。也就是说,a、b至关于num一、num2 的副本,副本的内容不管怎么修改,都不会影响到原件自己。

经过上面例子,咱们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用做为参数就不同,请看 example2.

example 2

public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5 };
        System.out.println(arr[0]);
        change(arr);
        System.out.println(arr[0]);
    }

    public static void change(int[] array) {
        // 将数组的第一个元素变为0
        array[0] = 0;
    }
1234567891011

结果

1
0
12

解析

img

array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 所以,外部对引用对象的改变会反映到所对应的对象上。

经过 example2 咱们已经看到,实现一个改变对象参数状态的方法并非一件难事。理由很简单,方法获得的是对象引用的拷贝,对象引用及其余的拷贝同时引用同一个对象。

不少程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的做者)认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。因为这种误解具备必定的广泛性,因此下面给出一个反例来详细地阐述一下这个问题。

example 3

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Student s1 = new Student("小张");
        Student s2 = new Student("小李");
        Test.swap(s1, s2);
        System.out.println("s1:" + s1.getName());
        System.out.println("s2:" + s2.getName());
    }

    public static void swap(Student x, Student y) {
        Student temp = x;
        x = y;
        y = temp;
        System.out.println("x:" + x.getName());
        System.out.println("y:" + y.getName());
    }
}
12345678910111213141516171819

结果

x:小李
y:小张
s1:小张
s2:小李
1234

解析

交换以前:

img

交换以后:

img

经过上面两张图能够很清晰的看出: 方法并无改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝

总结

Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。

下面再总结一下Java中方法参数的使用状况:

  • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型》
  • 一个方法能够改变一个对象参数的状态。
  • 一个方法不能让对象参数引用一个新的对象。

值传递和引用传递有什么区别

值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。

引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。

Java包

JDK 中经常使用的包有哪些

  • java.lang:这个是系统的基础类;
  • java.io:这里面是全部输入输出有关的类,好比文件操做等;
  • java.nio:为了完善 io 包中的功能,提升 io 包中性能而写的一个新包;
  • java.net:这里面是与网络有关的类;
  • java.util:这个是系统辅助类,特别是集合类;
  • java.sql:这个是数据库操做的类。

import java和javax有什么区别

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

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

IO流

java 中 IO 流分为几种?

  • 按照流的流向分,能够分为输入流和输出流;
  • 按照操做单元划分,能够划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上颇有规则,并且彼此之间存在很是紧密的联系, Java I0流的40多个类都是从以下4个抽象类基类中派生出来的。

  • InputStream/Reader: 全部的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 全部输出流的基类,前者是字节输出流,后者是字符输出流。

按操做方式分类结构图:

IO-操做方式分类

按操做对象分类结构图:

IO-操做对象分类

BIO,NIO,AIO 有什么区别?

简答

  • BIO:Block IO 同步阻塞式 IO,就是咱们日常使用的传统 IO,它的特色是模式简单使用方便,并发处理能力低。
  • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端经过 Channel(通道)通信,实现了多路复用。
  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操做基于事件和回调机制。

详细回答

  • BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动链接数不是特别高(小于单机1000)的状况下,这种模型是比较不错的,可让每个链接专一于本身的 I/O 而且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池自己就是一个自然的漏斗,能够缓冲一些系统处理不了的链接或请求。可是,当面对十万甚至百万级链接的时候,传统的 BIO 模型是无能为力的。所以,咱们须要一种更高效的 I/O 处理模型来应对更高的并发量。
  • NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N能够理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操做方法。 NIO提供了与传统BIO模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不一样的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持同样,比较简单,可是性能和可靠性都很差;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,能够使用同步阻塞I/O来提高开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操做以后会直接返回,不会堵塞在那里,当后台处理完成,操做系统会通知相应的线程进行后续的操做。AIO 是异步IO的缩写,虽然 NIO 在网络操做中,提供了非阻塞的方法,可是 NIO 的 IO 行为仍是同步的。对于 NIO 来讲,咱们的业务线程是在 IO 操做准备好时,获得通知,接着就由这个线程自行进行 IO 操做,IO操做自己是同步的。查阅网上相关资料,我发现就目前来讲 AIO 的应用还不是很普遍,Netty 以前也尝试使用过 AIO,不过又放弃了。

Files的经常使用方法都有哪些?

  • Files. exists():检测文件路径是否存在。
  • Files. createFile():建立文件。
  • Files. createDirectory():建立文件夹。
  • Files. delete():删除一个文件或目录。
  • Files. copy():复制文件。
  • Files. move():移动文件。
  • Files. size():查看文件个数。
  • Files. read():读取文件。
  • Files. write():写入文件。

反射与动态代理

反射

什么是反射机制?

简单说,反射机制值得是程序在运行时可以获取自身的信息。在java中,只要给定类的名字,那么就能够经过反射机制来得到类的全部信息。

JAVA反射机制是在运行状态中,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

静态编译和动态编译

  • 静态编译:在编译时肯定类型,绑定对象
  • 动态编译:运行时肯定类型,绑定对象

反射机制优缺点

  • 优势: 运行期类型的判断,动态加载类,提升代码灵活度。
  • 缺点: 性能瓶颈:反射至关于一系列解释操做,通知 JVM 要作的事情,性能比直接的java代码要慢不少。

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

反射是框架设计的灵魂。

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

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

Java获取反射的三种方法

1.经过new对象实现反射机制 2.经过路径实现反射机制 3.经过类名实现反射机制

public class Student {
    private int id;
    String name;
    protected boolean sex;
    public float score;
}
123456
public class Get {
    //获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一(经过创建对象)
        Student stu = new Student();
        Class classobj1 = stu.getClass();
        System.out.println(classobj1.getName());
        //方式二(所在经过路径-相对路径)
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());
        //方式三(经过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName());
    }
}

哪里用到反射机制?

jdbc中有一行代码:Class.forName('com.MySQL.jdbc.Driver.class').newInstance();那个时候只知道生成驱动对象实例,后来才知道,这就是反射,如今

不少框架都用到反射机制,hibernate,struts都是用反射机制实现的。

反射机制的优缺点?

静态编译:在编译时肯定类型,绑定对象,即经过

动态编译:运行时肯定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,体现了多态的应用,有利于下降类之间的耦合性。

一句话,反射机制的优势就是能够实现动态建立对象和编译,体现出很大的灵活性,特别是在J2EE的开发中

​ 它的灵活性就表现的十分明显。好比,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编

​ 译后,发布了,当发现须要更新某些功能时,咱们不可能要用户把之前的卸载,再从新安装新的版本,假如

​ 这样的话,这个软件确定是没有多少人用的。采用静态的话,须要把整个程序从新编译一次才能够实现功能

​ 的更新,而采用反射机制的话,它就能够不用卸载,只须要在运行时才动态的建立和编译,就能够实现该功

​ 能。

​ 它的缺点是对性能有影响。使用反射基本上是一种解释操做,咱们能够告诉JVM,咱们但愿作什么而且它

​ 知足咱们的要求。这类操做老是慢于只直接执行相同的操做。

反射的功能:

在运行时构造一个类的对象。

判断一个类所具备的成员变量和方法。

调用一个对象的方法。

生成动态代理。

反射的应用不少,不少框架都有用到:

反射的用途:

Spring 框架的 IoC 基于反射建立对象和设置依赖属性。

Spring MVC 的请求调用对应方法,也是经过反射。

JDBC 的 Class#forName(String className) 方法,也是使用反射。

反射中,Class.forName 和 ClassLoader 区别

java中class.forName()和classLoader均可用来对类进行加载。

class.forName()前者除了将类的.class文件加载到jvm中以外,还会对类进行解释,执行类中的static块。

而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

动态代理

什么是动态代理

代理类在程序运行时建立的代理方式被成为 动态代理。 也就是说,这种状况下,代理类并非在Java代码中定义的,而是在运行时根据咱们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优点在于能够很方便的对代理类的函数进行统一的处理,而不用修改每一个代理类的函数。

Java动态代理的两种实现方法

jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

总的来讲,反射机制在生成类的过程当中比较高效,而asm在生成类以后的相关执行过程当中比较高效(能够经过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。若是没有上述前提,jdk动态代理不能应用。由此能够看出,jdk动态代理有必定的局限性,cglib这种第三方类库实现的动态代理应用更加普遍,且在效率上更有优点。。

jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是经过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的做用,这样经过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是若是想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制。

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即便代理类没有实现任何接口也能够实现动态代理功能。CGLIB具备简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

为何要用动态代理

他能够在不修改别代理对象代码的基础上,经过扩展代理类,进行一些功能的附加与加强

静态代理与动态代理的区别

动态代理使咱们免于去重写接口中的方法,而着重于去扩展相应的功能或是方法的加强,与静态代理相比简单了很多,减小了项目中的业务量

动态代理机制

Proxy这个类的做用就是用来动态建立一个代理对象的类。每个动态代理类都必需要实现InvocationHandler这个接口,而且每一个代理类的实例都关联到了一个handler,当咱们经过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

序列化

什么是 Java 序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。

能够对流化后的对象进行读写操做,也可将流化后的对象传输于网络之间。

序列化是为了解决在对对象流进行读写操做时所引起的问题。

反序列化的过程,则是和序列化相反的过程。

另外,咱们不能将序列化局限在 Java 对象转换成二进制数组,例如说,咱们将一个 Java 对象,转换成 JSON 字符串,或者 XML 字符串,这也能够理解为是序列化。

如何实现 Java 序列化?

以下的方式,就是 Java 内置的序列化方案,实际场景下,咱们能够自定义序列化的方案,例如说 Google Protobuf 。

将须要被序列化的类,实现 Serializable 接口,该接口没有须要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的。

序列化

而后,使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象

接着,使用 ObjectOutputStream 对象的 #writeObject(Object obj) 方法,就能够将参数为 obj 的对象写出(即保存其状态)。

反序列化

要恢复的话则用输入流。

Java 序列话中,若是有些字段不想进行序列化怎么办?

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

当对象被序列化时,阻止实例中那些用此关键字修饰的的变量序列化。

当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。

transient 只能修饰变量,不能修饰类和方法。

经常使用API

String相关

字符型常量和字符串常量的区别

  1. 形式上: 字符常量是单引号引发的一个字符 字符串常量是双引号引发的若干个字符
  2. 含义上: 字符常量至关于一个整形值(ASCII值),能够参加表达式运算 字符串常量表明一个地址值(该字符串在内存中存放位置)
  3. 占内存大小 字符常量只占一个字节 字符串常量占若干个字节(至少一个字符结束标志)

什么是字符串常量池?

字符串常量池位于堆内存中,专门用来存储字符串常量,能够提升内存的使用率,避免开辟多块空间存储相同的字符串,在建立字符串时 JVM 会首先检查字符串常量池,若是该字符串已经存在池中,则返回它的引用,若是不存在,则实例化一个字符串放到池中,并返回其引用。

String 是最基本的数据类型吗

不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 之后引入的枚举类型也算是一种比较特殊的引用类型。

这是很基础的东西,可是不少初学者却容易忽视,Java 的 8 种基本数据类型中不包括 String,基本数据类型中用来描述文本数据的是 char,可是它只能表示单个字符,好比 ‘a’,‘好’ 之类的,若是要描述一段文本,就须要用多个 char 类型的变量,也就是一个 char 类型数组,好比“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};

可是使用数组过于麻烦,因此就有了 String,String 底层就是一个 char 类型的数组,只是使用的时候开发者不须要直接操做底层数组,用更加简便的方式便可完成对字符串的使用。

String有哪些特性

  • 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操做,其实都是建立一个新的对象,再把引用指向该对象。不变模式的主要做用在于当一个对象须要被多线程共享并频繁访问时,能够保证数据的一致性。
  • 常量池优化:String 对象建立以后,会在字符串常量池中进行缓存,若是下次建立一样的对象时,会直接返回缓存的引用。
  • final:使用 final 来定义 String 类,表示 String 类不能被继承,提升了系统的安全性。

String为何是不可变的吗?

简单来讲就是String类利用了final修饰的char类型数组存储字符,源码以下图因此:

/** The value is used for character storage. */
private final char value[];
12

String真的是不可变的吗?

我以为若是别人问这个问题的话,回答不可变就能够了。 下面只是给你们看两个有表明性的例子:

1) String不可变但不表明引用不能够变

String str = "Hello";
str = str + " World";
System.out.println("str=" + str);
123

结果:

str=Hello World
1

解析:

实际上,原来String的内容是不变的,只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说多开辟了一块内存区域给"Hello World"字符串。

2) 经过反射是能够修改所谓的“不可变”对象

// 建立字符串"Hello World", 并赋给引用s
String s = "Hello World";

System.out.println("s = " + s); // Hello World

// 获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");

// 改变value属性的访问权限
valueFieldOfString.setAccessible(true);

// 获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);

// 改变value所引用的数组中的第5个字符
value[5] = '_';

System.out.println("s = " + s); // Hello_World
123456789101112131415161718

结果:

s = Hello World
s = Hello_World
12

解析:

用反射能够访问私有成员, 而后反射出String对象中的value属性, 进而改变经过得到的value引用改变数组的结构。可是通常咱们不会这么作,这里只是简单提一下有这个东西。

是否能够继承 String 类

String 类是 final 类,不能够被继承。

String str="i"与 String str=new String(“i”)同样吗?

不同,由于内存的分配方式不同。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

String s = new String(“xyz”);建立了几个字符串对象

两个对象,一个是静态区的"xyz",一个是用new建立在堆上的对象。

String str1 = "hello"; //str1指向静态区
String str2 = new String("hello");  //str2指向堆上的对象
String str3 = "hello";
String str4 = new String("hello");
System.out.println(str1.equals(str2)); //true
System.out.println(str2.equals(str4)); //true
System.out.println(str1 == str3); //true
System.out.println(str1 == str2); //false
System.out.println(str2 == str4); //false
System.out.println(str2 == "hello"); //false
str2 = str1;
System.out.println(str2 == "hello"); //true
123456789101112

如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

示例代码:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba
12345678

数组有没有 length()方法?String 有没有 length()方法

数组没有 length()方法 ,有 length 的属性。String 有 length()方法。JavaScript中,得到字符串的长度是经过 length 属性获得的,这一点容易和 Java 混淆。

String 类的经常使用方法都有那些?

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

在使用 HashMap 的时候,用 String 作 key 有什么好处?

HashMap 内部实现是经过 key 的 hashcode 来肯定 value 的存储位置,由于字符串是不可变的,因此当建立字符串时,它的 hashcode 被缓存下来,不须要再次计算,因此相比于其余对象更快。

String, StringBuffer和StringBuilder区别

String是字符串常量,final修饰:StringBuffer字符串变量(线程安全);

StringBuilder 字符串变量(线程不安全)。

String和StringBuffer

String和StringBuffer主要区别是性能:String是不可变对象,每次对String类型进行操做都等同于产生了一个新的String对象,而后指向新的String对象。因此尽可能不在对String进行大量的拼接操做,不然会产生不少临时对象,致使GC开始工做,影响系统性能。

StringBuffer是对对象自己操做,而不是产生新的对象,所以在有大量拼接的状况下,咱们建议使用StringBuffer。

可是须要注意如今JVM会对String拼接作必定的优化:

String s=“This is only ”+”simple”+”test”会被虚拟机直接优化成String s=“This is only simple test”,此时就不存在拼接过程。

StringBuffer和StringBuilder

StringBuffer是线程安全的可变字符串,其内部实现是可变数组。StringBuilder是jdk 1.5新增的,其功能和StringBuffer相似,可是非线程安全。所以,在没有多线程问题的前提下,使用StringBuilder会取得更好的性能。

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

可变性

String类中使用字符数组保存字符串,private final char value[],因此string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。

线程安全性

String中的对象是不可变的,也就能够理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操做,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,因此是线程安全的。StringBuilder并无对方法进行加同步锁,因此是非线程安全的。

性能

每次对String 类型进行改变的时候,都会生成一个新的String对象,而后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象自己进行操做,而不是生成新的对象并改变对象引用。相同状况下使用StirngBuilder 相比使用StringBuffer 仅能得到10%~15% 左右的性能提高,但却要冒多线程不安全的风险。

对于三者使用的总结

若是要操做少许的数据用 = String

单线程操做字符串缓冲区 下操做大量数据 = StringBuilder

多线程操做字符串缓冲区 下操做大量数据 = StringBuffer

Date相关

包装类相关

自动装箱与拆箱

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

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

int 和 Integer 有什么区别

Java 是一个近乎纯洁的面向对象编程语言,可是为了编程的方便仍是引入了基本数据类型,可是为了可以将这些基本数据类型当成对象操做,Java 为每个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得两者能够相互转换。

Java 为每一个原始类型提供了包装类型:

原始类型: boolean,char,byte,short,int,long,float,double

包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

Integer a= 127 与 Integer b = 127相等吗

对于对象引用类型:比较的是对象的内存地址。
对于基本数据类型:
比较的是值。

若是整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false

public static void main(String[] args) {
    Integer a = new Integer(3);
    Integer b = 3;  // 将3自动装箱成Integer类型
    int c = 3;
    System.out.println(a == b); // false 两个引用没有引用同一对象
    System.out.println(a == c); // true a自动拆箱成int类型再和c比较
    System.out.println(b == c); // true

    Integer a1 = 128;
    Integer b1 = 128;
    System.out.println(a1 == b1); // false

    Integer a2 = 127;
    Integer b2 = 127;
    System.out.println(a2 == b2); // true
}

异常处理

error和exception有什么区别

error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能期望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。

exception 表示程序须要捕捉、须要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题

运行时异常和通常异常有何不一样

Java提供了两类主要的异常:runtimeException和checkedException

通常异常(checkedException)主要是指IO异常、SQL异常等。对于这种异常,JVM要求咱们必须对其进行cathc处理,因此,面对这种异常,无论咱们是否愿意,都是要写一大堆的catch块去处理可能出现的异常。

运行时异常(runtimeException)咱们通常不处理,当出现这类异常的时候程序会由虚拟机接管。好比,咱们历来没有去处理过NullPointerException,并且这个异常仍是最多见的异常之一。

出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇处处理代码,若是没有catch块进行处理,到了最上层,若是是多线程就有Thread.run()抛出,若是不是多线程那么就由main.run()抛出。抛出以后,若是是线程,那么该线程也就终止了,若是是主程序,那么该程序也就终止了。

其实运行时异常的也是继承自Exception,也能够用catch块对其处理,只是咱们通常不处理罢了,也就是说,若是不对运行时异常进行catch处理,那么结果不是线程退出就是主程序终止。

若是不想终止,那么咱们就必须捕获全部可能出现的运行时异常。若是程序中出现了异常数据,可是它不影响下面的程序执行,那么咱们就该在catch块里面将异常数据舍弃,而后记录日志。若是,它影响到了下面的程序运行,那么仍是程序退出比较好些。

Java中异常处理机制的原理

Java经过面向对象的方式对异常进行处理,Java把异常按照不一样的类型进行分类,并提供了良好的接口。当一个方法出现异常后就会抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法能够捕获到这个异常并对异常进行处理。Java的异常处理是经过5个关键词来实现的:try catch throw throws finally。

通常状况下是用try来执行一段程序,若是出现异常,系统会抛出(throws),咱们能够经过它的类型来捕捉它,或最后由缺省处理器来处理它(finally)。

try:用来指定一块预防全部异常的程序

catch:紧跟在try后面,用来捕获异常

throw:用来明确的抛出一个异常

throws:用来标明一个成员函数可能抛出的各类异常

finally:确保一段代码不管发生什么异常都会被执行的一段代码。

你平时在项目中是怎样对异常进行处理的。

(1)尽可能避免出现runtimeException 。例如对于可能出现空指针的代码,带使用对象以前必定要判断一下该对象是否为空,必要的时候对runtimeException

也进行try catch处理。

(2)进行try catch处理的时候要在catch代码块中对异常信息进行记录,经过调用异常类的相关方法获取到异常的相关信息,返回到web端,不只要给用户良好

的用户体验,也要能帮助程序员良好的定位异常出现的位置及缘由。例如,之前作的一个项目,程序遇到异常页面会显示一个图片告诉用户哪些操做致使程序出现

了什么异常,同时图片上有一个按钮用来点击展现异常的详细信息给程序员看的。

final、finally、finalize的区别

(1)、final用于声明变量、方法和类的,分别表示变量值不可变,方法不可覆盖,类不能够继承

(2)、finally是异常处理中的一个关键字,表示finally{}里面的代码必定要执行

(3)、finalize是Object类的一个方法,在垃圾回收的时候会调用被回收对象的此方法。

try()里面有一个return语句,那么后面的finally{}里面的code会不会被执行,何时执行,是在return前仍是return后?

你曾经自定义实现过异常吗?怎么写的?

throw和throws有什么区别?

throw关键字用来在程序中明确的抛出异常,相反,throws语句用来代表方法不能处理的异常。每个方法都必需要指定哪些异常不能处理,因此方法的调用者才可以确保处理可能发生的异常,多个异常是用逗号分隔的。

异常处理的时候,finally代码块的重要性是什么?

不管是否抛出异常,finally代码块老是会被执行。就算是没有catch语句同时又抛出异常的状况下,finally代码块仍然会被执行。最后要说的是,finally代码块主要用来释放资源,好比:I/O缓冲区,数据库链接。

异常的使用的注意地方?

不要将异常处理用于正常的控制流(设计良好的 API 不该该强迫它的调用者为了正常的控制流而使用异常)。

对能够恢复的状况使用受检异常,对编程错误使用运行时异常。

避免没必要要的使用受检异常(能够经过一些状态检测手段来避免异常的发生)。

优先使用标准的异常。

每一个方法抛出的异常都要有文档。

保持异常的原子性

不要在 catch 中忽略掉捕获到的异常。

请列出 5 个运行时异常?

NullPointerException

IndexOutOfBoundsException

ClassCastException

ArrayStoreException 当你试图将错误类型的对象存储到一个对象数组时抛出的异常

BufferOverflowException 写入的长度超出了容许的长度