Java常见问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java常见问题相关的知识,希望对你有一定的参考价值。
自古深情留不住,总是套路得人心
最近经历了一次惨无人道的程序员笔试,真的是“笔”试,默默地来整理一下……
以后遇到问题要多整理……
常见套路:
当一个变量被赋值为null的时候就被视为垃圾会被回收
当一个 .java 文件从第一行运行到最后一行时垃圾回收机制就会起作用,这是错误的,垃圾回收机制一直开着
if语句和suitch语句的区别(仅限十五字……):前者判断,后者选择
多态的好处
允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要有以下优点:
1. 可替换性:多态对已存在代码具有可替换性.
2. 可扩充性:增加新的子类不影响已经存在的类结构.
3. 接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的.
4. 灵活性:
5. 简化性:
实现多态主要有以下三种方式:
1. 接口实现
2. 继承父类重写方法
3. 同一类中进行方法重载
虚拟机是如何实现多态的
动态绑定技术(dynamic binding),执行期间判断所引用对象的实际类型,根据实际类型调用对应的方法.
接口的意义
接口的意义用三个词就可以概括:规范,扩展,回调.
抽象类的意义
抽象类的意义可以用三句话来概括:
1. 为其他子类提供一个公共的类型
2. 封装子类中重复定义的内容
3. 定义抽象方法,子类虽然有不同的实现,但是定义时一致的
接口和抽象类的区别
|比较|抽象类|接口|
|----|------|----|
|默认方法|抽象类可以有默认的方法实现|,java 8之前,接口中不存在方法的实现.|
|实现方式|子类使用extends关键字来继承抽象类.如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现.|子类使用implements来实现接口,需要提供接口中所有声明的实现.
|构造器|抽象类中可以有构造器,|接口中不能|
|和正常类区别|抽象类不能被实例化|接口则是完全不同的类型
|访问修饰符|抽象方法可以有public,protected和default等修饰|接口默认是public,不能使用其他修饰符
|多继承|一个子类只能存在一个父类|一个子类可以存在多个接口
|添加新方法|想抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码|如果往接口中添加新方法,则子类中需要实现该方法.|
父类的静态方法能否被子类重写
不能.重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏.
什么是不可变对象
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。
静态变量和实例变量的区别?
静态变量存储在方法区,属于类所有.实例变量存储在堆当中,其引用存在当前线程栈.
java 创建对象的几种方式
1. 采用new
2. 通过反射
3. 采用clone
4. 通过序列化机制
前2者都需要显式地调用构造方法. 造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解耦必先减少new的使用.
switch中能否使用string做参数
在idk 1.7之前,switch只能支持byte,short,char,int或者其对应的封装类以及Enum类型。从idk 1.7之后switch开始支持String.
switch能否作用在byte,long上?
可以用在byte上,但是不能用在long上.
String s1="ab",String s2="a"+"b",String s3="a",String s4="b",s5=s3+s4请问s5==s2返回什么?
返回false.在编译过程中,编译器会将s2直接优化为"ab",会将其放置在常量池当中,s5则是被创建在堆区,相当于s5=new String("ab");
你对String对象的intern()熟悉么?
intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回.
比如
String s1="aa";
String s2=s1.intern();
System.out.print(s1==s2);//返回false
Object中有哪些公共方法?
1. `equals()`
2. `clone()`
3. `getClass()`
4. `notify(),notifyAll(),wait()`
5. `toString`
java当中的四种引用
强引用,软引用,弱引用,虚引用.不同的引用类型主要体现在GC上:
1. 强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象
2. 软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
3. 弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象
4. 虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
a.hashCode()有什么用?与a.equals(b)有什么关系
hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。根据 Java 规范,使用 equal() 方法来判断两个相等的对象,必须具有相同的 hashcode。
将对象放入到集合中时,首先判断要放入对象的hashcode是否已经在集合中存在,不存在则直接放入集合.如果hashcode相等,然后通过equal()方法判断要放入对象与集合中的任意对象是否相等:如果equal()判断不相等,直接将该元素放入集合中,否则不放入.
3*0.1==0.3`返回值是什么
false,因为有些浮点数不能完全精确的表示出来。
如何正确的退出多层嵌套循环.
1. 使用标号和break;
2. 通过在外层循环中添加标识符
内部类的作用
内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立.在单个外围类当中,可以让多个内部类以不同的方式实现同一接口,或者继承同一个类.创建内部类对象的时刻不依赖于外部类对象的创建.内部类并没有令人疑惑的”is-a”关系,它就像是一个独立的实体.
内部类提供了更好的封装,除了该外围类,其他类都不能访问
int和Integer的区别
Integer是int的包装类型,在拆箱和装箱中,二者自动转换.int是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象.
int 和Integer谁占用的内存更多?
Integer 对象会占用更多的内存。Integer是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。
如何格式化日期?
Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期。DateFormat 类允许你使用多种流行的格式来格式化日期。参见答案中的示例代码,代码中演示了将日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。
常见问题:
final,finally和finalize的区别
final:最终的意思,可以修饰类,成员变量,成员方法
修饰类,类不能被继承
修饰变量,变量是常量
修饰方法,方法不能被重写
finally:是异常处理的一部分,用于释放资源。
一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了
finalize:是Object类的一个方法,用于垃圾回收
如果catch里面有return语句,finally里面的代码还会执行吗?
如果会,是在return前,还是return后。
会。前。
准确的说,应该是在中间。
基础问题:
常用排序
package com.hanqi.maya.model; import java.util.Arrays; public class Arr { public static void main(String[] args) { int arr1[]={0,1,2,3,4,5}; // 冒泡排序 for (int i = 0; i < arr1.length; i++) { for (int j = 0; j < arr1.length - 1; j++) { if (arr1[j] < arr1[j + 1]) { int temp = arr1[j + 1]; arr1[j + 1] = arr1[j]; arr1[j] = temp; } } } p(arr1); // 直接选择排序 int arr2[]={9,1,2,3,4,5}; for (int i = 0; i < arr2.length; i++) { int indexMax = i; for (int j = i + 1; j < arr2.length; j++) { if (arr2[indexMax] < arr2[j]) { indexMax = j; } } int temp = arr2[i]; arr2[i] = arr2[indexMax]; arr2[indexMax] = temp; } p(arr2); // 反转排序 int arr3[]={9,1,2,3,4,5}; int len = arr3.length; for (int i = 0; i < len / 2; i++) { int temp = arr3[i]; arr3[i] = arr3[len - i - 1]; arr3[len - i - 1] = temp; } p(arr3); } public static void p(int arr[]){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+","); } System.out.println(); } }
java语言的一个核心:
jdk, java development kits---面向开发人员
jre, java Runtime Environment---服务器上
Java的两个核心机制
java虚拟机---(以字节码为指令的CPU)---*.class
java编译器-->字节码-->类加载器进行验证-->虚拟机运行
垃圾回收机制
public class ...{
int a = 1;
}
c/c++垃圾回收由程序员去运行
Java编程语言(一门纯面向对象)的特点:
1, 面向对象
(1), 封装
(2), 继承
(3), 多态
2, 安全性
3, 跨平台,Java是一种跨平台的编程语言,一次编写,到处执行。
Java的三个版本
JavaSE:Java的标准版,主要用于桌面应用程序开发。同时也是Java程序的基础。比如Oracle11g的安装包,eclipseIDE开发工具,JavaSE包含了Java的基础,比如JDBC就是Java链接数据库的操作,以及网络通信多线程等技术。
JavaEE:是Java的企业版,主要用于企业级分布式网络程序,比如电子商务和ERP企业管理系统,其核心为企业Java组建模型。例如淘宝京东都是用JavaEE开发的。JavaEE包含了JavaSE的全部功能,我们可以在JavaEE中直接调用JavaSE的所有方法,比如在服务器上开启线程和数据流等等。
JavaME:主要应用于嵌入式系统的开发,例如手机等移动设备。
Java中的基础数据类型(四类八种):
1.整数型
byte----使用byte关键字来定义byte型变量,可以一次定义多个变量并对其进行赋值,也可以不进行赋值。byte型是整型中所分配的内存空间是最少的,只分配1个字节;取值范围也是最小的,只在-128和127之间,在使用时一定要注意,以免数据溢出产生错误。
short----short型即短整型,使用short关键字来定义short型变量,可以一次定义多个变量并对其进行赋值,也可以不进行赋值。系统给short型分配2个字节的内存,取值范围也比byte型大了很多,在-32768和32767之间,虽然取值范围变大,但是还是要注意数据溢出。
int----int型即整型,使用int关键字来定义int型变量,可以一次定义多个变量并对其进行赋值,也可以不进行赋值。int型变量取值范围很大,在-2147483648和2147483647之间,足够一般情况下使用,所以是整型变量中应用最广泛的。
long----long型即长整型,使用long关键字来定义long型变量,可以一次定义多个变量并对其进行赋值,也可以不进行赋值。而在对long型变量赋值时结尾必须加上“L”或者“l”,否则将不被认为是long型。当数值过大,超出int型范围的时候就使用long型,系统分配给long型变量8个字节,取值范围则更大,在-9223372036854775808和9223372036854775807之间。
2.浮点型
float----float型即单精度浮点型,使用float关键字来定义float型变量,可以一次定义多个变量并对其进行赋值,也可以不进行赋值。在对float型进行赋值的时候在结尾必须添加“F”或者“f”,如果不加,系统自动将其定义为double型变量。float型变量的取值范围在1.4E-45和3.4028235E-38之间。
double---double型即双精度浮点型,使用double关键字来定义double型变量,可以一次定义多个变量并对其进行赋值,也可以不进行赋值。在给double型赋值时,可以使用后缀“D”或“d”明确表明这是一个double类型数据,但加不加并没有硬性规定,可以加也可以不加。double型变量的取值范围在4.9E-324和1.7976931348623157E-308之间。
3.布尔型
boolean(true, false):布尔类型又称逻辑类型,只有两个值“true”和“false”,分别代表布尔逻辑中的“真”和“假”。使用boolean关键字声明布尔类型变量,通常被用在流程控制中作为判断条件。
4.字符型
char:char型既字符类型,使用char关键字进行声明,用于存储单个字符,系统分配两个字节的内存空间。在定义字符型变量时,要用单引号括起来,例如‘s’表示一个字符,且单引号中只能有一个字符,多了就不是字符类型了,而是字符串类型,需要用双引号进行声明。
基础数据类型变量和值全部存到栈,所以不能为空。
Java引用类型:
所有的类
所有的数组
所有的接口
数据类型相对的包装类:
byte---Byte
short---Short
int---Integer
long---Long
float---Float
double---Double
boolean---Boolean
char---Character
为什么要有包装类型?
包装类把基本类型数据转换为对象 :是因为Java是一个面向对象的语言,基本类型并不具有对象的性质,为了与其他对象“接轨”就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
包装类的作用:1.提供了一系列实用的方法
2.集合不允许存放基本数据类型数据,存放数字时,要用包装类型
数据类型之间的转转换
分为以下几种情况:
1)低级到高级的自动类型转换;
2)高级到低级的强制类型转换(会导致溢出或丢失精度);
3)基本类型向类类型转换;
4)基本类型向字符串的转换;
5)类类型向字符串转换
基本数据类型之间的转换规则
1.在一个双操作数以及位运算等算术运算式中,会根据操作数的类型将低级的数据类型自动转换为高级的数据类型,分为以下几种情况:
1)只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,并且结果也是double类型;
2)只要两个操作数中有一个是float类型的,另一个将会被转换成float类型,并且结果也是float类型;
3)只要两个操作数中有一个是long类型的,另一个将会被转换成long类型,并且结果也是long类型;
4)两个操作数(包括byte、short、int、char)都将会被转换成int类型,并且结果也是int类型。
2. 如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值,再做其它类型的自动转换。
3. 对于byte,short,char三种类型而言,他们是平级的,因此不能相互自动转换,可以使用下述的强制类型转换。 如:
short i=99 ;
char c=(char)i;
System.out.println("output:"+c);
4. 不能在布尔值和任何数字类型间强制类型转换;
5. 不同级别数据类型间的强制转换,可能会导致溢出或精度的下降。
6. 当字节类型变量参与运算,java作自动数据运算类型的提升,将其转换为int类型。例如:byte b;
b=3;
b=(byte)(b*3);//必须声明byte。
包装数据类型与基本数据类型之间的转换
简单类型的变量转换为相应的包装类,可以利用包装类的构造函数。即:Boolean(boolean value)、Character(char value)、Integer(int value)、Long(long value)、Float(float value)、Double(double value)
而在各个包装类中,总有形为××Value()的方法,来得到其对应的简单类型数据。利用这种方法,也可以实现不同数值型变量间的转换,例如,对于一个双精度实型类,intValue()可以得到其对应的整型变量,而doubleValue()可以得到其对应的双精度实型变量。
1.字符串与其它类型间的转换
⑴其它类型向字符串的转换
①调用类的串转换方法:X.toString();
②自动转换:X+“”;
③使用String的方法:String.volueOf(X);
⑵字符串作为值,向其它类型的转换
①先转换成相应的封装器实例,再调用对应的方法转换成其它类型
例如,字符中“32.1”转换double型的值的格式为:new Float(“32.1”).doubleValue()。也可以用:Double.valueOf(“32.1”).doubleValue()
②静态parseXXX方法
String s = "1";
byte b = Byte.parseByte( s );
short t = Short.parseShort( s );
int i = Integer.parseInt( s );
long l = Long.parseLong( s );
Float f = Float.parseFloat( s );
Double d = Double.parseDouble( s );
③Character的getNumericValue(char ch)方法
具体可查阅api。
转换实例
1)基本类型向类类型转换
正向转换:通过类包装器来new出一个新的类类型的变量
Integer a= new Integer(2);
反向转换:通过类包装器来转换
int b=a.intValue();
通过类包装器——>基本数据类型
eg1:int i=Integer.parseInt(“123”)
说明:此方法只能适用于字符串转化成整型变量
eg2: float f=Float.valueOf(“123”).floatValue()
说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。
eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。
eg4:double d=Double.valueOf(“123”).doubleValue()
说明:上例是将一个字符串转化成一个Double对象,然后再调用这个对象的doubleValue()方法返回其对应的double数值。
eg5: long l=Long.valueOf(“123”).longValue()
说明:上例是将一个字符串转化成一个Long对象,然后再调用这个对象的longValue()方法返回其对应的long数值。
eg6: char=Character.valueOf(“123”).charValue()
说明:上例是将一个字符串转化成一个Character对象,然后再调用这个对象的charValue()方法返回其对应的char数值。
2)基本类型向字符串的转换
正向转换:
如下:
System.out.println(""+2+3);// “""”把2转成字符串操作;
System.out.println(2+3); // 不存在转换。
System.out.println(2+3+"");// 前两个数值相加后,被“""”转成字符串。
System.out.println(2+""+3);// 同第一个。
输出显示为:23,5,5,23
3)类类型向字符串转换
正向转换:因为每个类都是object类的子类,而所有的object类都有一个toString()函数,所以通过toString()函数来转换即可
反向转换:通过类包装器new出一个新的类类型的变量
eg1: int i=Integer.valueOf(“123”).intValue()
说明:上例是将一个字符串转化成一个Integer对象,然后再调用这个对象的intValue()方法返回其对应的int数值。
eg2: float f=Float.valueOf(“123”).floatValue()
说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。
eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。
eg4:double d=Double.valueOf(“123”).doubleValue()
说明:上例是将一个字符串转化成一个Double对象,然后再调用这个对象的doubleValue()方法返回其对应的double数值。
eg5: long l=Long.valueOf(“123”).longValue()
说明:上例是将一个字符串转化成一个Long对象,然后再调用这个对象的longValue()方法返回其对应的long数值。
eg6: char=Character.valueOf(“123”).charValue()
说明:上例是将一个字符串转化成一个Character对象,然后再调用这个对象的charValue()方法返回其对应的char数值。
自己处理异常
try...catch...finally
自己编写处理代码,后面的程序可以继续执行
throws
抛出,把自己处理不了的,在方法上声明,告诉调用者,这里有问题
注意:try里面的代码越少越好
将问题包在try中,程序可以运行,但是catch里必须有代码,不然只能是隐藏问题而不是处理异常
throws和throw的区别
throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
面向对象三大特征
继承 封装 多态(包括抽象)
子类继承父类的属性和方法
所有类继承Object类
finalize 当垃圾收集确定不再有该对象的引用时,垃圾收集器在该对象上调用该对象,垃圾回收程序员控制不了
toString 在控制台打印会调用该方法
重载
同一个类,方法名相同,参数类型和个数不同,返回值不同不算重载
重写
子类重写父类
判断多态三个条件,满足一个既是多态
继承
重写
父类的引用指向子类的对象
super指向父类的引用
final
如我们定义
String s=“abc”;改变是改变他的引用 本身 abc是改变不了的
指向的变量不能改变
修饰的方法不能被重写
类不能被继承
对象转型
instanceof
父类的引用不能访问子类对象新增的成员
非要访问,强转 用(类型)的方式
abstract抽象方法
interface接口
类实现接口
class 类名 implements 接口名
多个无关的类可以实现同一个接口
一个类可以实现多个接口
接口中的属性默认也只能是 public static final
接口中只能定义没有被实现的方法
接口可以继承其他的接口
类和对象
对象是类的实例化
构造方法和成员方法
构造方法,是实例化一个类用的,没有返回值,不用void,方法名类名必须一模一样
成员方法
类与类之间的关系
关联关系 A类某个方法的对象是B类的某个对象
继承关系 类与类之间只能单继承,接口可以多继承
聚合关系
实现关系 抽象类,关键字 abstract 只定义方法,实现由子类实现,有方法被声明为抽象方法得类必须是抽象类
子类实现不了父类的抽象方法就在声明为抽象类,让他的子类再去实现
接口 可以多继承 类可以实现多个接口 只定义方法,实现有子类去做
两个接口方法重名,返回值相同,会被当做一个方法
返回值不同,编译报错
权限修饰符
实例化一个类用new关键字
Java中调用方法和属性只有用 .
成员变量写在类里
局部变量写在方法之中
成员变量类型的初始化默认值
局部变量初时必须赋值
构造函数,一个类继承另一个类,子类必须调用父类的构造方法,不写默认无参数的构造方法,如果写了一个有参数的,会将空参的构造方法顶替
this关键字,当前对象的引用,代表类的从开始到结束
static静态的,修饰的不在堆栈里,在数据区
栈读取数据块,基础数据类型,和引用都在此
静态变量只有一个值,一个改动其他都改动,应当用类名.调用,不管实例化多少都共享一个
静态方法不能访问非静态成员
以上是关于Java常见问题的主要内容,如果未能解决你的问题,请参考以下文章
LockSupport.java 中的 FIFO 互斥代码片段