参考兔老大的问题整理的java知识点:
1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?
一个.java源文件可以包括多个类,但是只能有一个类是public类型的,且必须和文件名一致
这是编译器规定的:
1、因为每个.java源文件实质上是一个编译单元(文件),而每个编译单元都需要一个入口,可以说是一个调用的入口。
2、public类名必须和文件名一致,不然编译器就会报错;
3、如果.java源文件内,没有public类,那么可以存在多个类,并且类名和文件名无关,文件名可以随便写。
可以这么理解:
每个.java源文件,会被编译成一个个.class文件,如果一个.java源文件存在多个public类,那么就相当于生成了入口,调用的时候,将会不知道去调用哪个入口,所以是不被允许的;
而如果文件名和public类名不一致,生成的调用接口对应不上,所以也不被允许;
最后,如果一个.java源文件含有多个非public类,编译完没有入口,也就没法调用,虽然编译是没问题的,但不知其作用。
2、short s1= 1; s1= s1+1; 有没有错?
3、short s1= 1; s1 += 1;有没有错?
第一题:short s1 = 1; s1 = s1 + 1;
错!
- s1 + 1,s1是short类型,1是int型,s1会自动转换为int型的1,与1相加后,得到int型的2,要向左侧的short类型的s1看齐,即需要通过强制类型转换。正确写法:s1 = (short) (s1 + 1);
第二题:short s1 = 1; s1 += 1;
正确!
- 执行s1+=1;其实执行的是s1 = (short) (s1 + 1); 其中会有一个强制转换的过程。
第三题:short s1=1,s2=1;short s3=s1+s2;
错误!
- 这里是编译器从数据安全方面考虑,如果s1和s2都是较大的short类型数,可能会导致溢出,所以会要求强制转换到int。正确写法:short s3 = (int)(s1 + s2);
4、使用final关键字修饰一个变量时,引用的内容一定不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,但是引用变量所指向的对象中的内容还是可以改变的
其实这个还是跟final关键字的使用有关。final关键字除了可以修饰类和方法以外,final关键字还可以用来修饰变量,其中包括基本数据类型的值变量和引用变量。
final修饰的变量其实就相当于定义了一个常量,无法被修改的变量。
如果final修饰的是一个基本数据类型的值变量,那么这个变量的值就定了,不能变了,比如final int i=10;那么就是在栈内存中对i变量赋值为10,这个值是不能改变的了;
而如果final关键字修饰的是一个引用变量,那么该变量在栈内存中存的是一个内存地址,该地址就不能变了,但是该内存地址所指向的那个对象还是可以变的。
如果拿去买房为例的话,对于基本数据类型的变量,房子就好比是被final修饰的基本数据类型的变量,一旦定死了就不能修改了(虽然现实中可以修改的)
而对于引用变量,这个地皮(栈内存地址)一旦规划作为住宅用地了,那么便不可以再做其他用途(引用不能指向其他的对象),但是在这个地址上盖什么样的房子,多少房子是可以改变的(引用对象的内容可以改变)
5、是否可以从static方法内对非static方法调用?为什么?
不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方 法调用,而static方法调用时不需要创建对象,可以直接调用。
6、Overload和Override的区别?
Override(重载的意思)(指同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同))注意事项:
1.覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果
2.覆盖的方法的返回值必须和被覆盖的方法的返回一致
3.覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4.被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖
Overload(覆盖的意思,也就是重写)注意事项:
1.在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
2.不能通过访问权限、返回类型、抛出的异常进行重载;
3.方法的异常类型和数目不会对重载造成影响;
4.对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果
细节:
总结: override(重写,覆盖) (发生在子类与父类中) 1、方法名、参数、返回值相同。 2、子类方法不能缩小父类方法的访问权限。 3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。 4、存在于父类和子类之间。 5、方法被定义为final不能被重写。
overload(重载,过载) (发生在同一个方法中) 1、参数类型、个数、顺序至少有一个不相同。 2、不能重载只有返回值不同的方法名。 3、存在于父类和子类、同类中
7、Overloaded的方法是否可以改变返回值的类型?
如果两个方法的参数列表完全一样,是否可以让它们的返回值不同来实现重载Overload
- 这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然remove方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java就无法确定编程者倒底是想调用哪个方法了,(你调用的时候没有说也不能说返回值类型)因为它无法通过返回结果类型来判断
8、接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?
概念:
接口: 接口是一种约束形式,其中只包括成员定义,不包含成员实现的内容。
抽象类: 抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
抽象方法: 抽象方法指一些只有方法声明,而没有具体方法体的方法。抽象方法一般存在于抽象或接口中。
结果:
接口可以继承(extends)接口
public interface InterfaceA { } interface InterfaceB extends InterfaceA{ }
抽象类可以实现(implements)接口
public interface InterfaceA { } abstract class TestA implements InterfaceA{ }
抽象类可继承(extends)实体类,(但前提是实体类必须有明确的构造函数)
public class TestA{ } abstract class TestB extends TestA{ }
9、Java中实现多态的机制是什么?
Java多态的实现机制是什么
Java中实现多态的机制是依靠父类或接口的引用指向子类,从而实现一个对象多种形态的特性,其父类的引用是在运行时动态的指向具体的实例,调用该引用的方法时,不是根据引用变量的类型中定义的方法来运行,而是根据具体的实例的方法。
10、abstractclass和interface有什么区别?
含有abstract修饰符的class即为抽象类,abstract 类不能创建的实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final
下面比较一下两者的语法区别:
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然
eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
5. 抽象类中可以包含静态方法,接口中不能包含静态方法
6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
11、String s = "Hello";s = s + "world!";执行后,原始的String对象中的内容变了没有?
没有
因为String类是不可变类(immutable class)。不可变类,顾名思义就是说类的实例是不可被修改的。实例的信息是在创建的时候提供,并且在整个生命周期中都不可改变。在这段代码中,s原来指向一个String对象,内容是“hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为”helloworld!",原来那个对象还存在内存中,只是s这个引用变量不再指向他了。
通过上面的说明,我们很容易得出一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为,String对象建立后不能改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,他允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer。
12、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";
创建了一个
解析:
- String s = “a” + “b” + “c” + “d” + “e”; 赋值符号右边的"a"、“b”、“c”、“d”、“e"都是常量 对于常量,编译时就直接存储它们的字面值而不是它们的引用 在编译时就直接将它们连接的结果提取出来变成了"abcde” 该语句在class文件中就相当于String s = “abcde” 然后当JVM执行到这一句的时候, 就在String pool里找 如果没有这个字符串,就会产生一个
如果改成 String s = a+b+c+d+e; 呢,又是几个了?(就是说上面是因为 “a”、“b”、“c”、“d”、"e"都是常量,但如果是变量呢? )
是3个对象,但只有一个String对象
由于编译器的优化,最终代码为通过StringBuilder完成:
StringBuilder builder = new StringBuilder(); builder.append(a); builder.append(b); builder.append(c); builder.append(d); builder.append(e); String s = builder.toString();
我们先看看StringBuilder的构造器
public StringBuilder() { super(16); } //看下去 AbstractStringBuilder(int capacity) { value = new char[capacity]; }
总结:三个对象分别为
1 StringBuilder
2 new char[capacity]
3 new String(value,0,count);如果说String对象,则为1个
13、final, finally, finalize的区别
-
性质不同
-
final为关键字;
-
finalize()为方法;
-
finally为为区块标志,用于try语句中;
-
-
作用
- final为用于标识常量的关键字,final标识的关键字存储在常量池中;
- finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象 进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行 I/0操作);
- finally{}用于标识代码块,与try{ }进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行 .
-
final的用法
被final修饰的类不可以被继承 被final修饰的方法不可以被重写 被final修饰的变量不可以被改变,如果修饰引用,那么表示引用不可变,引用指向的内容可变. 被final修饰的方法,JVM会尝试将其内联,以提高运行效率 被final修饰的常量,在编译阶段会存入常量池中 除此之外,编译器对final域要遵守的两个重排序规则更好:
在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序,初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序
-
finally的用法
finally是在异常处理中的使用的
不管 try 语句块正常结束还是异常结束,finally 语句块是保证要执行的.
如果 try 语句块正常结束,那么在 try 语句块中的语句都执行完之后,再执行 finally 语句块.
不管有没有出现异常,finally块中的代码都会执行;
当try和catch中有return时,finally仍然会执行;
finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,无论finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定好的;
finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值.
finalize的用法
finalize() 是Java中Object的一个protected方法.返回值为空,当该对象被垃圾回收器回收时,会调用该方法.
关于finalize()函数
finalize不等价于c++中的析构函数;
对象可能不被垃圾机回收器回收;
垃圾回收不等于析构;
垃圾回收只与内存有关;
垃圾回收和finalize()都是靠不住的,只要JVM还没有快到耗尽内存的地步,它是不会浪费时间进行垃圾回收的;
程序强制终结后,那些失去引用的对象将会被垃圾回收.(System.gc())
finalize()的用途:比如当一个对象代表了打开了一个文件,在对象被回收前,程序应该要关闭该文件,可以通过finalize函数来发现未关闭文件的对象,并对其进行处理.
14、error和exception有什么区别?
-
Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
-
Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
-
Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
-
Exception 又分为 可检查 (checked)异常和 不检查 (unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是 Throwable 不是 Exception。
-
不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。
15、Java 中堆和栈区别
最主要的区别就是栈内存用来存储局部变量和方法调用。
而堆内存用来存储Java中的对象。
无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。
独有还是共享
栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
异常错误
如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError。
而如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。
空间大小
栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。如果递归没有及时跳出,很可能发生StackOverFlowError问题。
你可以通过-Xss选项设置栈内存的大小。-Xms选项可以设置堆的开始时的大小,-Xmx选项可以设置堆的最大值。
这就是Java中堆和栈的区别。理解好这个问题的话,可以对你解决开发中的问题,分析堆内存和栈内存使用,甚至性能调优都有帮助。
查看默认值(Updated)
查看堆的默认值,使用下面的代码,其中InitialHeapSize为最开始的堆的大小,MaxHeapSize为堆的最大值。
13:17 $ java -XX:+PrintFlagsFinal -version | grep HeapSize
uintx ErgoHeapSizeLimit = 0 {product}
uintx HeapSizePerGCThread = 87241520 {product}
uintx InitialHeapSize := 134217728 {product}
uintx LargePageHeapSizeThreshold = 134217728 {product}
uintx MaxHeapSize := 2147483648 {product}
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
查看栈的默认值,其中ThreadStackSize为栈内存的大小。
13:21 $ java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
intx CompilerThreadStackSize = 0 {pd product}
intx ThreadStackSize = 1024 {pd product}
intx VMThreadStackSize = 1024 {pd product}
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
16、能将 int 强制转换为 byte 类型的变量吗?(引申到所有大类型转小类型)
可以但是会舍去3个字节的数
17、hashCode有什么用?与 a.equals(b) 有什么关系?
equals相等两个对象,则hashcode一定要相等。但是hashcode相等的两个对象不一定equals相等。
hashcode只能说是标识对象,在hash算法中可以将对象相对离散开,这样就可以在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不一定是唯一的,所以hash算法中定位到具体的链表后,需要循环链表,然后通过equals方法来对比Key是否是一样的。
18,java中会存在内存泄漏吗
理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。
大佬们多多关照