JAVA开发工程师面试题,第一篇=>JAVA基础
- 第一篇=>JAVA基础篇
- 一. JDK,JRE,JVM区别?
- 作用
- 1. JKD(Java Development Kit)
- 2. JRE(Java Runtime Environment)
- 3. JVM(Java Virtual Machine)
- 区别
- 三者联系:
- 三者区别:
- 二. = 、== 和 equals 的区别是什么?
- 作用
- = :java中的赋值运算
- == :
- 1.用于原始(基础)数据类型的比较时=>比较他们的值。
- 2.用于引用数据类型时=>比较的是其内存中的地址。
- String类:
- 为什么Integer类中1000 == 1000为false而100 == 100为true?
- equals()方法:
- 三.两个对象的 hashCode()相同,则 equals()也一定为 true吗
- 什么是hashCode()?
- 作用
- 什么是equals()?
- 例子
- 四.final 在 java 中有什么作用
- 一、final关键字的基本用法
- 1、修饰类
- 2、修饰方法
- 3、修饰变量
- 二、深入理解final关键字
- 1、类的final变量和普通变量有什么区别?
- 2、被final修饰的引用变量指向的对象内容可变吗?
- 3、final参数的问题
- 五.java 中的 Math.round(-1.5) 等于多少?
- 六. String 属于基础的数据类型吗?
- 补充提问: void是否属于基础数据类型?
- 七. java 中操作字符串都有哪些类?它们之间有什么区别?
- 八.String str="i"与 String str=new String(“i”)一样吗?
- 补充问题:String s = new String("abc")创建了几个String对象?
- 补充问题:你对String对象的intern()熟悉么?
- 补充问题:以下代码中,s5==s2返回值是什么?
- 九.如何将字符串反转?
- 十.String 类的常用方法都有那些?
- 十一.抽象类必须要有抽象方法吗?
- 十二.普通类和抽象类有哪些区别?
- 十三.抽象类能使用 final 修饰吗?
- 十四. 接口和抽象类有什么区别?
- 区别:
- 十五.java 中 IO 流分为几种?
- 十六. BIO、NIO、AIO 有什么区别?
- 十七.Files的常用方法都有哪些?
- 十八. & 和 &&的区别
- 十九. 在.java文件内部可以有多少类(非内部类)?
- 二十. 如何正确的退出多层嵌套循环?
- 二十一. 内部类有什么作用?
- 二十二. clone()是哪个类的方法?
- 二十三.static都有哪些用法?
- 二十四.3*0.1==0.3返回值是什么
- 二十五. a=a+b与a+=b有什么区别吗?
- 二十六. 了解泛型么?简述泛型的上界和下界?
- 二十七. 重载和重写的区别?
- 二十八.Java 面向对象编程三大特性?
- 封装 继承 多态
- 封装:
- 继承:(extends )
- 多态:
第一篇=>JAVA基础篇
你好! 此系列文章献给广大找工作中和准备找工作的JAVA开发者们。
此系列文章适用于准备面试初,中,高级别的java开发程序员。
此文章分析方式由浅入深,答题方式先抓住中心思想然后再展开来说。
各位看官如有意见或建议请留言或直接发邮件于a10000000081@163.com。
本文仅供参考,如果您需解决具体问题(尤其法律、医学等领域),建议您详细咨询相关领域专业人士。
一. JDK,JRE,JVM区别?
作用
1. JKD(Java Development Kit)
JDK是java开发工具包=>在目录下面有 六个文件夹、一个src类库源码压缩包、和其他几个声明文件。其中,真正在运行java时起作用的 是以下四个文件夹:bin、include、lib、 jre。有这样一个关系,JDK包含JRE,而JRE包 含JVM。
> bin:最主要的是编译器(javac.exe)
include:java和JVM交互用的头文件
lib:类库
jre:java运行环境
2. JRE(Java Runtime Environment)
JRE是Java运行环境=>其不包含任何开发工具(如编译器和调试器),JRE是指java运行环境。光有JVM还不能成class的 执行,因为在解释class的时候JVM需要调用解释所需要的类库lib。 (jre里有运行.class的java.exe)。
3. JVM(Java Virtual Machine)
即java虚拟机, java运行时的环境,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。针对java用户,也就是拥有可运行的.class文件包(jar或者war)的用户。里面主要包含了jvm和java运行时基本类库(rt.jar)。rt.jar可以简单粗暴地理解为:它就是java源码编译成的jar包。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。
区别
三者联系:
JVM不能单独搞定class的执行,解释class的时候JVM需要调用解释所需要的类库lib。在JDK下面的的jre目录里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为jre。JVM+Lib=JRE。总体来说就是,我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析这些字节码,映射到CPU指令集或OS的系统调用。
三者区别:
a.JDK和JRE区别:在bin文件夹下会发现,JDK有javac.exe而JRE里面没有,javac指令是用来将java文件编译成class文件的,这是开发者需要的,而用户(只需要运行的人)是不需要的。JDK还有jar.exe, javadoc.exe等等用于开发的可执行指令文件。这也证实了一个是开发环境,一个是运行环境。
b.JRE和JVM区别:JVM并不代表就可以执行class了,JVM执行.class还需要JRE下的lib类库的支持,尤其是rt.jar。
参考文献
二. = 、== 和 equals 的区别是什么?
作用
= :java中的赋值运算
// 例如
int a = 1;
== :
1.用于原始(基础)数据类型的比较时=>比较他们的值。
2.用于引用数据类型时=>比较的是其内存中的地址。
String类:
String a = "abc";
String b = "abc";
System.out.println(a == b);//true
String str1= "hello";
String str2= new String("hello");
String str3= str2;
System.out.println(str1 == str2);//false
System.out.println(str3 == str2);//truec
从图中可以发现每个String对象的内容实际是保存到堆内存中的,而且堆中的内容是相等的,但是对于str1和str2来说所指向的地址堆内存地址是不等的,所以尽管内容是相等的,但是地址值是不相等的
“ == ” 是用来进行数值比较的,所以str1和str2比较不相等,因为str2和str3指向同一个内存地址所以str2和str3是相等的。所以“==”是用来进行地址值比较的。
为什么Integer类中1000 == 1000为false而100 == 100为true?
Integer i1 = 100, i2 = 100;
System.out.println(i1 == i2);//true
Integer i3 = 1000, i4 = 1000;
System.out.println(i3 == i4);//fales
查看Integer.java类,会发现有一个内部私有类,IntegerCache.java,它缓存了从-128到127之间的所有的整数对象。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
private IntegerCache() {
}
static {
int var0 = 127;
String var1 = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
int var2;
if(var1 != null) {
try {
var2 = Integer.parseInt(var1);
var2 = Math.max(var2, 127);
var0 = Math.min(var2, 2147483518);
} catch (NumberFormatException var4) {
;
}
}
high = var0;
cache = new Integer[high - -128 + 1];
var2 = -128;
for(int var3 = 0; var3 < cache.length; ++var3) {
cache[var3] = new Integer(var2++);
}
assert high >= 127;
}
}
所以例子中i1和i2指向了一个对象。因此100==100为true。
equals()方法:
//equals()方法在object类中定义如下:
public boolean equals(Object obj) {
return (this == obj);
}
用来判断其他的对象是否和该对象相等。
但在一些诸如String,Integer,Date类中把Object中的这个方法覆盖了,作用被覆盖为比较内容是否相同。
// 比如在String类中如下:
public boolean equals(Object var1) {
if(this == var1) {
return true;
} else {
if(var1 instanceof String) {
String var2 = (String)var1;
int var3 = this.value.length;
if(var3 == var2.value.length) {
char[] var4 = this.value;
char[] var5 = var2.value;
for(int var6 = 0; var3-- != 0; ++var6) {
if(var4[var6] != var5[var6]) {
return false;
}
}
return true;
}
}
return false;
}
}
很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Math、Integer、Double等这些类都是重写了equals()方法的,从而进行的是内容的比较。当然,基本类型是进行值的比较。
三.两个对象的 hashCode()相同,则 equals()也一定为 true吗
不对
在java中,equals和hashcode是有设计要求的,equals相等,则hashcode一定相等,反之则不然。
为何会有这样的要求?
在集合中,比如HashSet中,要求放入的对象不能重复,怎么判定呢?
首先会调用hashcode,如果hashcode相等,则继续调用equals,也相等,则认为重复。
如果重写equals后,如果不重写hashcode,则hashcode就是继承自Object的,返回内存编码,这时候可能出现equals相等,而hashcode不等,你的对象使用集合时,就会等不到正确的结果
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
什么是hashCode()?
hashCode()方法是Object类中的一个方法,但是使用native关键字修饰,我们直接在源码中是什么都看不到的.
public native int hashCode();
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
具体可以参照:Java中native关键字 和 在 Windows 中实现 Java 本地方法
在这个基础面试题中我们就不讲如何查看native修饰的方法了,我们可以参照一下百度百科 ,解释的还是挺详细的.
作用
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是根据一个哈希函数返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。既然是根据函数返回,那么极有可能在两个不同的对象计算之后产生相同的哈希值。
而若 equls 返回为 true , 则两者的hashcode一定相等,即相等的对象具有相等的哈希码。
什么是equals()?
这个可以参考我的上一篇文章java中==和equals的区别是什么?
小结
当你知道了什么是hashCode()与什么是equals()的时候这个答案其实就出来了,hashCode(方法)是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值,equals()是Object中的方法,默认返回==的比较值,但是两个方法都是可以可以被重写的,所以一定为true么?当然是不一定了.
例子
String str1 = "通话";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
执行的结果:
str1:1179395 | str2:1179395
false
代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 却为 false
因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
四.final 在 java 中有什么作用
谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法。
一、final关键字的基本用法
在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。
1、修饰类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
2、修饰方法
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。即父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。
final修饰的方法表示此方法已经是“最后的、最终的”含义,亦即此方法不能被重写(可以重载多个final修饰的方法)。此处需要注意的一点是:因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。(注:类的private方法会隐式地被指定为final方法。)
3、修饰变量
修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。
final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。
二、深入理解final关键字
在了解了final关键字的基本用法之后,这一节我们来看一下final关键字容易混淆的地方。
1、类的final变量和普通变量有什么区别?
当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。
2、被final修饰的引用变量指向的对象内容可变吗?
引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的
3、final参数的问题
在实际应用中,我们除了可以用final修饰成员变量、成员方法、类,还可以修饰参数、若某个参数被final修饰了,则代表了该参数是不可改变的。如果在方法中我们修改了该参数,则编译器会提示你:The final local variable i cannot be assigned. It must be blank and not using a compound assignment。
java采用的是值传递,对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。
五.java 中的 Math.round(-1.5) 等于多少?
Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后做向下取整。
测试结果如下
public class test {
public static void main(String[] args){
System.out.println(Math.round(1.0)); // 1
System.out.println(Math.round(1.1)); // 1
System.out.println(Math.round(1.2)); // 1
System.out.println(Math.round(1.3)); // 1
System.out.println(Math.round(1.4)); // 1
System.out.println(Math.round(1.5)); // 2
System.out.println(Math.round(1.6)); // 2
System.out.println(Math.round(1.7)); // 2
System.out.println(Math.round(1.8)); // 2
System.out.println(Math.round(1.9)); // 2
System.out.println(Math.round(-1.0)); // -1
System.out.println(Math.round(-1.1)); // -1
System.out.println(Math.round(-1.2)); // -1
System.out.println(Math.round(-1.3)); // -1
System.out.println(Math.round(-1.4)); // -1
System.out.println(Math.round(-1.5)); // -1
System.out.println(Math.round(-1.6)); // -2
System.out.println(Math.round(-1.7)); // -2
System.out.println(Math.round(-1.8)); // -2
System.out.println(Math.round(-1.9)); // -2
}
}
参考文献:
六. String 属于基础的数据类型吗?
前面的题中说过String是final修饰的java类,并非基础的数据类型
字符类型:byte,char
基本整型:short,int,long
浮点型:float,double
布尔类型:boolean
补充提问: void是否属于基础数据类型?
java.lang.Class.isPrimitive() 确定指定的Class对象表示一个基本类型。有九种预定义的Class对象代表的八个基本类型和void。这些都是由Java虚拟机创建的,并且具有相同的名称,它们代表即boolean, byte, char, short, int, long, float, 和double 等原始类型。
public static void main(String[] args) {
System.out.println(void.class.isPrimitive()); //true
System.out.println(boolean.class.isPrimitive()); //true
System.out.println(char.class.isPrimitive()); //true
System.out.println(byte.class.isPrimitive()); //true
System.out.println(short.class.isPrimitive()); //true
System.out.println(int.class.isPrimitive()); //true
System.out.println(long.class.isPrimitive()); //true
System.out.println(float.class.isPrimitive()); //true
System.out.println(double.class.isPrimitive()); //true
System.out.println(Boolean.class.isPrimitive()); //false
System.out.println(Character.class.isPrimitive()); //false
System.out.println(Byte.class.isPrimitive()); //false
System.out.println(Short.class.isPrimitive()); //false
System.out.println(Integer.class.isPrimitive()); //false
System.out.println(Float.class.isPrimitive()); //false
System.out.println(Long.class.isPrimitive()); //false
System.out.println(Double.class.isPrimitive()); //false
}
所以void到底是否属于基础数据类型就仁者见仁智者见智吧。
七. java 中操作字符串都有哪些类?它们之间有什么区别?
Java 中,常用的对字符串操作的类有 String、StringBuffer、StringBuilder
String : final 修饰,String 类的方法都是返回 new String。即对 String 对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。
StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。
StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以 new StringBuilder 对象,调用 StringBuilder 对象的 append()、replace()、delete() 等方法修改字符串。String和StringBuffer主要区别是性能:String是不可变对象,每次对String类型进行操作都等同于产生了一个新的String对象,然后指向新的String对象.所以尽量不要对String进行大量的拼接操作,否则会产生很多临时对象,导致GC开始工作,影响系统性能.
StringBuffer是对象本身操作,而不是产生新的对象,因此在有大量拼接的情况下,我们建议使用StringBuffer(线程安全).
需要注意现在JVM会对String拼接做一定的优化,比如
八.String str="i"与 String str=new String(“i”)一样吗?
不一样
因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”)方式,则会被分到堆内存中。
java String str1 = "i";
String str2 = "i";
String str3 = new String("i");
System.out.println(str1 == str2);//ture
System.out.println(str2 == str3);//false
解释:
- Java 虚拟机会将其分配到常量池中:常量池不会重复创建对象。
在String str1="i"中,把i值存在常量池,地址赋给str1。假设再写一个String str2=“i”,则会把i的地址赋给str2,但是i对象不会重新创建,他们引用的是同一个地址值,共享同一个i内存。
- 分到堆内存中:堆内存会创建新的对象。
假设再写一个String str3=new String(“i”),则会创建一个新的i对象,然后将新对象的地址值赋给str3。虽然str3和str1的值相同但是地址值不同。
拓展:
堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。
补充问题:String s = new String(“abc”)创建了几个String对象?
2个.一个是字符串字面常数,在字符串常量池中;另一个是new出来的字符串对象,在堆中.
补充问题:你对String对象的intern()熟悉么?
Stirng中的intern()是个Native方法,它会首先从常量池中查找是否存在该常量值的字符串,若不存在则先在常量池中创建,否则直接返回常量池已经存在的字符串的引用. 比如
String s1="aa";
String s2=s1.intern();
System.out.print(s1==s2);
补充问题:以下代码中,s5==s2返回值是什么?
String s1="ab";
String s2="a"+"b";
String s3="a";
String s4="b";
String s5=s3+s4;
返回false.在编译过程中,编译器会将s2直接优化为"ab",将其放置在常量池当中;而s5则是被创建在堆区,相当于s5=new String(“ab”);
九.如何将字符串反转?
- 利用 StringBuffer 或 StringBuilder 的 reverse 成员方法
// StringBuffer
public static String reverse1(String str) {
return new StringBuilder(str).reverse().toString();
}
- 利用 String 的 toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接:
// toCharArray
public static String reverse2(String str) {
char[] chars = str.toCharArray();
String reverse = "";
for (int i = chars.length - 1; i >= 0; i--) {
reverse += chars[i];
}
return reverse;
}
- 利用 String 的 CharAt 方法取出字符串中的各个字符:
// charAt
public static String reverse3(String str) {
String reverse = "";
int length = str.length();
for (int i = 0; i < length; i++) {
reverse = str.charAt(i) + reverse;
}
return reverse;
}
- 最后补充 main 方法中的测试代码:
public static void main(String[] args) {
String s = "abc123";
System.out.println("----------------");
for (int i = s.length() - 1; i >= 0; i--) {
System.out.print(s.charAt(i));
}
System.out.println("----------------");
System.out.println("变换前: " + s);
System.out.println("变换后: " + reverse1(s));
System.out.println("变换后: " + reverse2(s));
System.out.println("变换后: " + reverse3(s));
}
十.String 类的常用方法都有那些?
- equals:字符串是否相同
- equalsIgnoreCase:忽略大小写后字符串是否相同
- compareTo:根据字符串中每个字符的Unicode编码进行比较
- compareToIgnoreCase:根据字符串中每个字符的Unicode编码进行忽略大小写比较
- indexOf:目标字符或字符串在源字符串中位置下标
- lastIndexOf:目标字符或字符串在源字符串中最后一次出现的位置下标
- valueOf:其他类型转字符串
- charAt:获取指定下标位置的字符
- codePointAt:指定下标的字符的Unicode编码
- concat:追加字符串到当前字符串
- isEmpty:字符串长度是否为0
- contains:是否包含目标字符串
- startsWith:是否以目标字符串开头
- endsWith:是否以目标字符串结束
- format:格式化字符串
- getBytes:获取字符串的字节数组
- getChars:获取字符串的指定长度字符数组
- toCharArray:获取字符串的字符数组
- join:以某字符串,连接某字符串数组
- length:字符串字符数
- matches:字符串是否匹配正则表达式
- replace:字符串替换
- replaceAll:带正则字符串替换
- replaceFirst:替换第一个出现的目标字符串
- split:以某正则表达式分割字符串
- substring:截取字符串
- toLowerCase:字符串转小写
- toUpperCase:字符串转大写
- trim:去字符串首尾空格
十一.抽象类必须要有抽象方法吗?
一种模板模式。抽象类为所有子类提供了一个通用模板,子类可以在这个模板基础上进行扩展。
通过抽象类,可以避免子类设计的随意性。
- 抽象类必须有关键字abstract来修饰。
- 抽象类可以不含有抽象方法。
- 如果一个类包含抽象方法,则该类必须是抽象类。
- 抽象类不能实例化,即不能用new来实例化抽象类。
- 抽象类可以包含属性、方法、构造方法。但构造方法不能用来new实例,只能用来被子类调用。
- 抽象类只能用来继承。
- 抽象方法必须被子类实现。
十二.普通类和抽象类有哪些区别?
- 抽象类不能被实例化
- 抽象类可以有抽象方法,抽象方法只需申明,无需实现
- 含有抽象方法的类必须申明为抽象类
- 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
- 抽象方法不能被声明为静态
- 抽象方法不能用private修饰
- 抽象方法不能用final修饰
十三.抽象类能使用 final 修饰吗?
- 不能,抽象类是被用于继承的,final修饰代表不可修改、不可继承的。
十四. 接口和抽象类有什么区别?
他们都不能实例化对象,都可以包含抽象方法,而且抽象方法必须被继承的类全部实现。
区别:
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
参数 | 抽象类 | 接口 |
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 接口是完全不同的类型访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 接口方法默认修饰符是public。你不可以使用其它修饰符。 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。(java8以后接口可以有default和static方法,所以可以运行main方法) |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 | 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 | 如果你往接口中添加方法,那么你必须改变实现该接口的类。 |
抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
一个类只能继承一个抽象类,而一个类却可以实现多个接口
参考:
十五.java 中 IO 流分为几种?
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的角色划分为节点流和处理流。
Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java IO流的40多个类都是从如下4个抽象类基类中派生出来的。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作方式分类结构图:
按操作对象分类结构图:
十六. BIO、NIO、AIO 有什么区别?
- 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模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞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()写入文件
十八. & 和 &&的区别
基础的概念不能弄混:&是位操作,&&是逻辑运算符.需要记住逻辑运算符具有短路特性,而&不具备短路特性.来看看以下代码执行结果?
public class Test{
static String name;
public static void main(String[] args){
if(name!=null&name.equals("")){
System.out.println("ok");
}else{
System.out.println("erro");
}
}
}
十九. 在.java文件内部可以有多少类(非内部类)?
在一个java文件中只能有一个public公共类,但是可以有多个default修饰的类.
二十. 如何正确的退出多层嵌套循环?
- 使用标号和break;
- 通过在外层循环中添加标识符
二十一. 内部类有什么作用?
内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立.在单个外围类当中,可以让多个内部类以不同的方式实现同一接口,或者继承同一个类.创建内部类对象的时刻不依赖于外部类对象的创建.内部类并没有令人疑惑的”is-a”关系,它就像是一个独立的实体.此外,内部类提供了更好的封装,除了该外围类,其他类都不能访问.
二十二. clone()是哪个类的方法?
java.lang.Cloneable 是一个标示性接口,不包含任何方法.clone ()方法在 Object 类中定义的一个Native方法:
protected native Object clone() throws CloneNotSupportedException;
二十三.static都有哪些用法?
所有的人都知道static关键字这两个基本的用法:静态变量和静态方法.也就是被static所修饰的变量/方法都属于类的静态资源,类实例所共享.
除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作:
public calss PreCache{
static{
//执行相关操作
}
}
此外static也多用于修饰内部类,此时称之为静态内部类.
最后一种用法就是静态导包,即import static.import static是在JDK 1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名,可以直接使用资源名,比如:
import static java.lang.Math.*;
public class Test{
public static void main(String[] args){
//System.out.println(Math.sin(20));传统做法
System.out.println(sin(20));
}
}
二十四.3*0.1==0.3返回值是什么
System.out.println(0.1*3==0.3); //false
System.out.println(3*0.1 == 0.3); // false
System.out.println(3*0.1); // 0.30000000000000004
System.out.println(4*0.1); // 0.4
System.out.println(4*0.1==0.4); // true
System.out.println(1*0.3); // 0.3
System.out.println(1*0.3 == 0.3); // true
可以看出3 * 0.1造成了精度损失,至于好多网友张口就来的3 * .1F,3 * .1f,(float) 3 * .1是误导性的说法,其实这个跟强转并无关系,亲测, 不信大家可以试试。
详情请参照:
二十五. a=a+b与a+=b有什么区别吗?
+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换.如:
byte a = 127;
byte b = 127;
b = a + b; // 报编译错误:cannot convert from int to byte
b += a;
二十六. 了解泛型么?简述泛型的上界和下界?
有时候希望传入的类型有一个指定的范围,从而可以进行一些特定的操作,这时候就需要通配符了?在Java中常见的通配符主要有以下几种:
- <?>: 无限制通配符
- <? extends E>: extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
- <? super E>: super关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型.
- < ? extends E>: 用于灵活读取,使得方法可以读取 E 或 E 的任意子类型的容器对象。
- < ? super E>: 用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象。
用简单的一句话来概括就是为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符,使用的规则就是:生产者有上限(读操作使用extends),消费者有下限(写操作使用super).
二十七. 重载和重写的区别?
- 重载: 发生在同一个类中,方法名必须相同,实质表现就是多个具有不同的参数个数或者类型的同名函数(返回值类型可随意,不能以返回类型作为重载函数的区分标准),返回值类型、访问修饰符可以不同,发生在编译时。
- 重写: 发生在父子类中,方法名、参数列表必须相同,是父类与子类之间的多态性,实质是对父类的函数进行重新定义。返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
问:Java 构造方法能否被重写和重载?
重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行,所以 Java 的构造方法是不能被重写的。而重载是针对同一个的,所以构造方法可以被重载。
二十八.Java 面向对象编程三大特性?
封装 继承 多态
封装:
封装就是把抽象的数据和对数据进行的操作封装在一起,数据被保存在内部,程序的其他部分只有通过被授权的操作(成员方法)才能对数据进行操作。
java提供了四种控制修饰符控制方法和变量访问的权限:
- public:对外公开
- protected:对子类和同一包中的类公开
- 没有修饰符号:向同一个包的类公开
- private:只有类本身可以访问,不对外公开
继承:(extends )
继承是使用已存在的类的定义作为基础建立新类的技术。继承可以解决代码复用问题,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extend语句来声明继承父类。
关于继承如下 3 点请记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
多态:
所谓多态,就是指一个引用(类型)在不同情况下的多种状态,你也可以这样理解:父类型的引用指向子类型的对象。
多态有两个好处:
- 应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承
- 派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。 //多态的真正作用,