Java面试问题总结带答案(Java基础)

问题总结(均在网上搜索和书本摘抄所得,如若侵权请联系立即删除)

java基础

++i与i++有什么区别

它们的不同点在于i++是在程序执行完毕后进行自增,而++i是在程序开始执行前进行自增
完毕后、开始前)

java中==和equals和hashCode的区别

  • ==是运算符,用来比较两个值、两个对象的内存地址是否相等;
  • equals是Object类的方法,默认情况下比较两个对象是否是同一个对象,内部实现是通过“==”来实现的;
  • hashCoed也是Object类里面的方法,返回值是一个对象的哈希码,同一个对象哈希码一定相等,但不同对象哈希码也有可能相等。

int与integer的区别

1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0

抽象类的意义

抽象类: 一个类中如果包含抽象方法,这个类应该用abstract关键字声明为抽象类。
意义:
1,为子类提供一个公共的类型;
2,封装子类中重复内容(成员变量和方法);
3,定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的。

接口和抽象类的区别

区别如下:
抽象类:
1、抽象类使用abstract修饰;
2、抽象类不能实例化,即不能使用new关键字来实例化对象;
3、含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字修饰;
4、抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;
5、抽象类中的抽象方法只有方法体,没有具体实现;
接口:
1、接口使用interface修饰;
2、接口不能被实例化;
3、一个类可以实现多个接口;
4、接口中的方法均为抽象方法;
5、接口中不能包含实例或静态方法(静态方法必须实现,接口中方法是抽象方法,不能实现)。
在这里插入图片描述

什么时候使用抽象类和接口

  • 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类。
  • 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
  • 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

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

我们是可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝,最常见的例子就是对象中包含一个日期对象的引用。
详细见这里

谈谈对java多态的理解

  • 面向对象的三大基本特征:封装、继承、多态
  • 多态是指:父类引用指向子类对象,在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。(同一消息可以根据发送对象的不同而采用多种不同的行为方式。)
  • 多态的作用:消除类型之间的耦合关系。
  • 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
  • 实现多态的三要素:继承,重写,父类引用指向子类对象(即,声明是父类,实际指向的是子类的一个对象)

String、StringBuffer、StringBuilder区别和适用环境

  • String:String 是不可变的对象, 因此在每次对 String 类型进行改变的时候,都会生成一个新的 String对象,然后将指针指向新的 String 对象。
  • StringBuffer:每次都会对 StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。线程安全,效率低。 StringBuilder:在进行String拼接时需要额外创建一个
  • StringBuffer,之后将StringBuffer 转换为 String,若采用 StringBuffer,则不需额外创建StringBuffer。线程不安全,效率高。
    适用环境
    (1)如果要操作少量的数据用 String;
    (2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
    (3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

泛型中extends和super的区别

在Java泛型中,?表示通配符,即未知。

1:?extends T 表示上界类型通配符。

2:?super T 表示下界类型通配符。
在这里插入图片描述

进程和线程的区别

1、进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位)
2、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
3、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
4、但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

final,finally,finalize的区别

一、final :

1、修饰符(关键字):
如果一个类被声明为final,意味着它不能再派生新的子类,不能作为父类被继承。因此一个类不能及被声明为abstract,又被声明为final的。

2、将变量或方法声明为final,可以保证他们使用中不被改变。被声明为final的变量必须在声明时给定初值,而以后的引用中只能读取,不可修改,被声明为final的方法也同样只能使用,不能重载。
二、finally:

在异常处理时提供finally块来执行清楚操作。如果抛出一个异常,那么相匹配的catch语句就会执行,然后控制就会进入finally块,如果有的话。

三、finalize:

是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。这个方法是在垃圾收集器在确定了,被清理对象没有被引用的情况下调用的。

finalize是在Object类中定义的,因此,所有的类都继承了它。子类可以覆盖finalize()方法,来整理系统资源或者执行其他清理工作。

序列化的方式

什么是序列化?

内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。将数据对象转换为二进制流的过程称为对象的序列化(Serialization)。反之,将二进制流恢复为数据对象的过程称为反序列化(Deserialization)。

序列化的三种方式

  • 序列化方式一: 实现Serializable接口(隐式序列化) 通过实现Serializable接口,这种是隐式序列化(不需要手动),这种是最简单的序列化方式,会自动序列化所有非static和
    transient关键字修饰的成员变量。
  • 序列化方式二:实现Externalizable接口。(显式序列化) Externalizable接口继承自Serializable, 我们在实现该接口时,必须实现writeExternal()和readExternal()方法,而且只能通过手动进行序列化,并且两个方法是自动调用的,因此,这个序列化过程是可控的,可以自己选择哪些部分序列化
  • 序列化方式三:实现Serializable接口+添加writeObject()和readObject()方法。(显+隐序列化) 如果想将方式一和方式二的优点都用到的话,可以采用方式三,
    先实现Serializable接口,并且添加writeObject()和readObject()方法。注意这里是添加,不是重写或者覆盖。但是添加的这两个方法必须有相应的格式。

string 转换成 integer的方式及原理

  • 调用Integer类的parseInt()方法。
  • 调用Character类的digit方法。把字符ch换算成int类型,即对应的ASCII码值。

静态属性和静态方法是否可以被继承?是否可以被重写?以及原因

  • 父类的静态属性和方法可以被子类继承
  • 不可以被子类重写:当父类的引用指向子类时,使用对象调用静态方法或者静态变量,是调用的父类中的方法或者变量。并没有被子类改写。
  • 原因:
    • 因为静态方法从程序开始运行后就已经分配了内存,也就是说已经写死了。所有引用到该方法的对象(父类的对象也好子类的对象也好)所指向的都是同一块内存中的数据,也就是该静态方法。
    • 子类中如果定义了相同名称的静态方法,并不会重写,而应该是在内存中又分配了一块给子类的静态方法,没有重写这一说。

成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

  • 成员内部类:定义在类内部的非静态类叫作成员内部类。(成员内部类不能定义静态方法和变量(final修饰的除外),因为成员内部类是非静态的,而在Java的非静态代码块中不能定义静态方法和变量。)
  • 静态内部类:定义在类内部的静态类被称为静态内部类。(静态内部类可以访问外部类的静态变量和方法;在静态内部类中可以定义静态变量、方法、构造函数等;静态内部类通过“外部类.静态内部类”的方式来调用。)
  • 局部内部类:定义在方法中的类叫作局部内部类。(当一个类只需要在某个方法中使用某个特定的类时,可以通过局部类来优雅地实现。)
  • 匿名内部类:指通过继承一个父类或者实现一个接口的方式直接定义并使用的类。(匿名内部类没有class关键字,这是因为匿名内部类直接使用new生成一个对象的引用。)

讲一下常见编码方式?

ASCII码:共有128个,用一个字节的低7位表示

ISO8859-1:在ASCII码的基础上涵盖了大多数西欧语言字符,仍然是单字节编码,它总共能表示256个字符

GB2312:全称为《信息交换用汉字编码字符集基本集》,它是双字节编码,总的编码范围是A1~F7

  • A1~A9 ·符号区
  • B0~F7 汉字区

GBK:数字交换用汉字编码字符集》,它可能是单字节、双字节或者四字节编码,与GB2312编码兼容

UTF-16:具体定义了Unicode字符在计算机中的存取方法。采用2字节来表示Unicode转化格式,它是定长的表示方法,不论什么字符都可以用两个字节表示

UTF-8: UTF-8采用一种变长技术,每个编码区域有不同的字码长度,不同的字符可以由1~6个字节组成。

  • 如果一个字节,最高位为0,表示这是一个ASCII字符(00~7F)
  • 如果一个字节,以11开头,连续的1的个数暗示这个字符的字节数

如何格式化日期?

Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期。DateFormat类允许你使用多种流行的格式来格式化日期。

Java的异常体系

在Java中,Throwable是所有错误或异常的父类,Throwable又可分为Error和Exception,常见的Error有AWTError、ThreadDeath,Exception又可分为RuntimeException和CheckedException,如图所示:
在这里插入图片描述

什么是异常链

当异常中抛出异常时,防止先前的异常信息丢失就用到异常链。
A异常抛出B异常前,通过B异常对象调用initCause方法传入A异常对象,再抛出B异常。接收B异常的catch通过调用getCause()能获取A异常信息(即引发B异常的异常)。这样A异常在上抛过程就不会丢失。

throw和throws的区别

Throw:
1.作用在方法内,表示抛出具体异常,由方法体内的语句处理。
2.具体向外抛出的动作,所以它抛出的是一个异常实体类。若执行了Throw一定是抛出了某种异常。
Throws:
1.作用在方法的声明上,表示如果抛出异常,则由该方法的调用者来进行异常处理。
2.主要的声明这个方法会抛出会抛出某种类型的异常,让它的使用者知道捕获异常的类型。
3.出现异常是一种可能性,但不一定会发生异常。

说说你对Java反射的理解(未写)

反射的原理,反射创建类实例的三种方式是什么(没写完)

当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。我们认为java并不是动态语言,但是它却有一个非常突出的动态相关机制,俗称:反射。
IT行业里这么说,没有反射也就没有框架,现有的框架都是以反射为基础。

java当中的四种引用

(1)强引用:在Java中最常见的就是强引用。在把一个对象赋给一个引用变量时,这个引用变量就是一个强引用。有强引用的对象一定为可达性状态,所以不会被垃圾回收机制回收。因此,强引用是造成Java内存泄漏(MemoryLink)的主要原因。
(2)软引用:软引用通过SoftReference类实现。如果一个对象只有软引用,则在系统内存空间不足时该对象将被回收。
(3)弱引用:弱引用通过WeakReference类实现,如果一个对象只有弱引用,则在垃圾回收过程中一定会被回收。
(4)虚引用:虚引用通过PhantomReference类实现,虚引用和引用队列联合使用,主要用于跟踪对象的垃圾回收状态。

深拷贝和浅拷贝的区别是什么?、

简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!

什么是编译器常量?使用它有什么风险?

编译期常量指的就是程序在编译时就能确定这个常量的具体值
非编译期常量就是程序在运行时才能确定常量的值,因此也称为运行时常量
定义上来说,声明为final类型的基本类型或String类型并直接赋值(非运算)的变量就是编译期常量

 //编译时常量
final int i = 4;
final String str = "dasd";

// 非编译期常量
final String str1 = new String("dasd");


Random rand = new Random(47);
//这个也是非编译期常量,为运行时常量
final int i4 = rand.nextInt(20);

这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,部署了一个新的jar,但是你的客户端仍然在使用老的值。为了避免这种情况,当你在更新依赖
JAR 文件时,确保重新编译你的程序。这里的更新JAR文件,重新编译程序是为了更新你使用的编译期常量的值!

你对String对象的intern()熟悉么?

intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回。
比如
String s1=”aa”;
String s2=s1.intern();
System.out.print(s1==s2);//返回true

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

一、性能方面   
a=a+b是加法运算 需要两次寻找地址而a+=b是增量运算有寄存器优先时 只有一次地址查找。效率方面后者略高于前者
基于现在计算机的发展可忽略不计。

二、可读性方面   
两者都是赋值运算,一般情况下可以认为两者没有什么区别
但前者与数学算法描述更接近相对来说更严谨而后者书写更快捷但可读性下降。

三、数据类型方面   
两者写法上主要在于是否能进行数据类型自动转换,事实上就是类型与精度上的差异。   
eg:当两个操作数数据类型一致时两种形式的运算结果没有差别 但数据类型不同时 且a值的数据类型精度低时 此时两种形式就有区别了。+=会自动转换。

静态代理和动态代理的区别,什么场景使用?

静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理类:在程序运行时,运用反射机制动态创建而成。
详细点这里

Java中实现多态的机制是什么?

本质上多态分两种:

  • 编译时多态(又称静态多态)
  • 运行时多态(又称动态多态)

重载(overload)就是编译时多态的一个例子,编译时多态在编译时就已经确定,运行时运行的时候调用的是确定的方法。
我们通常所说的多态指的都是运行时多态,也就是编译时不确定究竟调用哪个具体方法,一直延迟到运行时才能确定。这也是为什么有时候多态方法又被称为延迟方法的原因。

下面简要介绍一下运行时多态(以下简称多态)的机制。
多态通常有两种实现方法:

  • 子类继承父类(extends)
  • 类实现接口(implements)

无论是哪种方法,其核心之处就在于对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。
要使用多态,在声明对象时就应该遵循一条法则:

  • 声明的总是父类类型或接口类型,创建的是实际类型。
  • 在定义方法参数时也通常总是应该优先使用父类类型或接口类型。

详细点这里

如何将一个Java对象序列化到文件里?

1)对象需要实现Seralizable接口
2)通过ObjectOutputStream的writeObject()方法写入和ObjectInputStream的readObject()方法来进行读取

说说你对Java注解的理解

注解的概念注解(Annotation)

是Java提供的设置程序中元素的关联信息和元数据(MetaData)的方法,它是一个接口,程序可以通过反射获取指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息。

标准元注解:@Target、@Retention、@Documented、@Inherited
元注解(Meta-Annotation)负责注解其他注解。在Java中定义了 4个标准的元注解类型@Target、@Retention、@Documented、@Inherited,用于定义不同类型的注解。

  1. @Target:@Target说明了注解所修饰的对象范围。
    在这里插入图片描述

  2. @Retention:@Retention定义了该注解被保留的级别,即被描述的注解在什么级别有效,有以下3种类型。
    ◎ SOURCE:在源文件中有效,即在源文件中被保留。
    ◎ CLASS:在Class文件中有效,即在Class文件中被保留。
    ◎ RUNTIME:在运行时有效,即在运行时被保留。

  3. @Documented:@Documented表明这个注解应该被javadoc工具记录,因此可以被javadoc类的工具文档化。

  4. @Inherited:@Inherited是一个标记注解,表明某个被标注的类型是被继承的。如果有一个使用了@Inherited修饰的Annotation被用于一个Class,则这个注解将被用于该Class的子类。

说说你对依赖注入的理解

依赖注入就是一种代码的组织形式
可以把他们当做一个一个模块
每个模块之间看不到彼此
然后有一个中间商
他来负责把模块组合起来
模块之间就是靠接口来对接

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
谁依赖于谁:当然是应用程序依赖于IoC容器;
为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

说一下泛型原理,并举例说明

泛型的本质是参数化类型,泛型提供了编译时类型的安全检测机制,该机制允许程序在编译时检测非法的类型,比如要实现一个能够对字符串(String)、整形(Int)、浮点型(Float)、对象(Object)进行大小比较的方法,就可以使用Java泛型。
使用泛型的好处是在编译期就能够检查类型是否安全,同时所有强制性类型转换都是自动和隐式进行的,提高了代码的安全性和重用性。

Java中String的了解

1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。
2)String类其实是通过char数组来保存字符串的。
3)String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何改变操作都会生成新的对象。

String为什么要设计成不可变的?

第一:在Java程序中String类型是使用最多的,这就牵扯到大量的增删改查,每次增删改差之前其实jvm需要检查一下这个String对象的安全性,就是通过hashcode,当设计成不可变对象时候,就保证了每次增删改查的hashcode的唯一性,也就可以放心的操作。
第二:网络连接地址URL,文件路径path通常情况下都是以String类型保存, 假若String不是固定不变的,将会引起各种安全隐患。
第三:字符串值是被保留在常量池中的,也就是说假若字符串对象允许改变,那么将会导致各种逻辑错误。字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。

Object类的equal和hashCode方法重写,为什么?

重写equals方法必须重写hashcode方法,这是为什么呢?
首先equals与hashcode间的关系是这样的:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)

由于为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值