Java零散记录



  • String能被继承吗

    不能,因为String是这样定义的:public final class String extends Object,里边有final关键字,所以不能被继承。

  • 什么样的类不能被继承?

     一,在Java中,只要是被定义为final的类,也可以说是被final修饰的类,就是不能被继承的。

     二,final是java中的一个关键字,可以用来修饰变量、方法和类。用关键词final修饰的域成为最终域。用关键词final修饰的变量一旦赋值,就不能改变,也称为修饰的标识为常量。如果一个类的域被关键字final所修饰,它的取值在程序的整个执行过程中将不会改变。

     三,假如说整个类都是final,就表明自己不希望从这个类继承,或者不答应其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。

  • String/StringBuffer/StringBuilder区别

    Java 平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer和StringBulder类表示的字符串对象可以直接进行修改。StringBuilder是JDK1.5引入的,它和StringBuffer的方法完全相同,区别在于它是单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer略高。

    总结:

    String是不可变对象,每次改变都生成新对象

    StringBuffer 不生成新对象,字符串经常改变情况下,推荐使用

    StringBuilder 线程不安全,效率高

  • 静态内部类/匿名内部类/内部类

    静态内部类:使用static修饰的内部类

    匿名内部类:使用new生成的内部类

    内部类持有外部类的引用,因为内部类的产生依赖于外部类,持有的引用是类名.this。

  • switch是否能作用在byte上,是否能作用在long上,是否能作用在String上?

    switch支持使用byte类型,不支持long类型,String支持在java1.7引入

  • 接口的意义

    规范、扩展、回调

  • 覆盖equals时总要覆盖hashCode方法

    因为如果对象根据equals方法是比较相等的,那么调这两个对象中的任意一个对象的hashCode方法都必须产生相同的效果

  • 多态的好处

    可替换性、可扩充、接口性、灵活、简化

  • 线程阻塞

  • String转int

    int i = Integer.parseInt([String])

  • 格式化

1
2
3
4
5
float a = 0.5534
DecimalFormat fnum = new DecimalFormat("##0.0");
String dd = fnum.format() + "%";
结果为:0.5

  • JVM参数

-Xmx128m:设置JVM最大可用内存为128M。

-Xms128m:设置JVM最小内存为128m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小。 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更 多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。


  • Hashcode的作用。

http://c610367182.iteye.com/blog/1930676

以Java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样:

  1. new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。
  1. 比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.

  • try catch finally,try里有return,finally还执行么?

会执行,在方法 返回调用者前执行。Java允许在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是纪录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,这会对程序造成很大的困扰,C#中就从语法规定不能做这样的事。


  • Excption与Error区别

Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的状况;Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。


  • final关键字的作用

相信对于final的用法,大多数人都可以随口说出三句话:

1、被final修饰的类不可以被继承

2、被final修饰的方法不可以被重写

3、被final修饰的变量不可以被改变

被final修饰的变量,不管变量是在是哪种变量,切记不可变的是变量的引用而非引用指向对象的内容。另外,本文中关于final的作用还有两点没有讲到:

1、被final修饰的方法,JVM会尝试为之寻求内联,这对于提升Java的效率是非常重要的。因此,假如能确定方法不会被继承,那么尽量将方法定义为final的。

2、被final修饰的常量,在编译阶段会存入调用类的常量池中。


  • 可变长度数组的原理

当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。

ArrayList:是按照原数组的50%延长。构造一个初始容量为 10 的空列表。

Vector:是按照原数组的100%延长。


  • 哈希表的原理:

1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。

2,哈希值就是这个元素的位置。

3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

4,存储哈希值的结构,我们称为哈希表。

5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。
这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。


  • StringBuffer 和 StringBuilder

JDK1.5出现StringBuiler;构造一个其中不带字符的字符串生成器,初始容量为 16 个字符。该类被设计用作 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。

方法和StringBuffer一样;

StringBuffer 和 StringBuilder 的区别:

StringBuffer线程安全。

StringBuilder线程不安全。

单线程操作,使用StringBuilder 效率高。

多线程操作,使用StringBuffer 安全。


  • 什么情况下finally的代码不执行

System.exit(0); //退出jvm,只有这种情况finally不执行。


  • 当同步函数被static修饰时,这时的同步用的是哪个锁呢?

    静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。

    所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。
    这个对象就是 类名.class

  • Map集合存储和Collection有着很大不同:

    Collection一次存一个元素;Map一次存一对元素。

    Collection是单列集合;Map是双列集合。

    Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。

    特点:要保证map集合中键的唯一性。

  • 想要获取map中的所有元素:

    原理:map中是没有迭代器的,collection具备迭代器,只要将map集合转成Set集合,可以使用迭代器了。之所以转成set,是因为map集合具备着键的唯一性,其实set集合就来自于map,set集合底层其实用的就是map的方法。

    把map集合转成set的方法:

    Set keySet();

    Set entrySet();//取的是键和值的映射关系。


  • 静态方法可以被重写么?

能!静态方法是和类绑定的,虽然能复写,但是可能得到的不是你想要的效果。

如果从重写方法会有什么特点来看,我们是不能重写静态方法的。虽然就算你重写静态方法,编译器也不会报错。也就是说,如果你试图重写静态方法,Java不会阻止你这么做,但你却得不到预期的结果(重写仅对非静态方法有用)。重写指的是根据运行时对象的类型来决定调用哪个方法,而不是根据编译时的类型。让我们猜一猜为什么静态方法是比较特殊的?因为它们是类的方法,所以它们在编译阶段就使用编译出来的类型进行绑定了。使用对象引用来访问静态方法只是Java设计者给程序员的自由。我们应该直接使用类名来访问静态方法,而不要使用对象引用来访问。


  • Person p = new Person();创建一个对象都在内存中做了什么事情?

1:先将硬盘上指定位置的Person.class文件加载进内存。

2:执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p。

3:在堆内存中开辟一个实体空间,分配了一个内存首地址值。new

4:在该实体空间中进行属性的空间分配,并进行了默认初始化。

5:对空间中的属性进行显示初始化。

6:进行实体的构造代码块初始化。

7:调用该实体对应的构造函数,进行构造函数初始化。()

8:将首地址赋值给p ,p变量就引用了该实体。(指向了该对象)


  • 构造函数为什么要super第一行,

    用this调用构造函数,必须定义在构造函数的第一行。因为构造函数是用于初始化的,所以初始化动作一定要执行。否则编译失败。

  • java.lang.Object

Object:所有类的直接或者间接父类,Java认为所有的对象都具备一些基本的共性内容,这些内容可以不断的向上抽取,最终就抽取到了一个最顶层的类中的,该类中定义的就是所有对象都具备的功能。

具体方法:

1,boolean equals(Object obj):用于比较两个对象是否相等,其实内部比较的就是两个对象地址。如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果

2,String toString()

3,Class getClass():获取任意对象运行时的所属字节码文件对象。

4,int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表的性能。将该对象的内部地址转换成一个整数来实现的。

通常equals,toString,hashCode,在应用中都会被复写,建立具体对象的特有的内容。


  • 小小面试题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //1
    new Object(){
    void show(){
    System.out.println("show run");
    }
    }.show(); //写法和编译都没问题
    //2
    Object obj = new Object(){
    void show(){
    System.out.println("show run");
    }
    };
    obj.show(); //写法正确,编译会报错

1和2的写法正确吗?有区别吗?说出原因。

写法是正确,1和2都是在通过匿名内部类建立一个Object类的子类对象。

区别:

第一个可是编译通过,并运行。

第二个编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,就被提升为了Object类型,而编译时检查Object类中是否有show方法,所以编译失败。


  • 在Serializable类中的serialVersionUID的作用

    private static final long serialVersionUID = -1672970955045193907L;

SUID不是一个对象的哈希值(翻译错了,公司一个牛逼同事提醒了!),是源类的哈希值。如果类更新,例如域的改变,SUID会变化,这里有4个步骤:

1.忽略SUID,相当于运行期间类的版本上的序列化和反序列上面没有差异。

2.写一个默认的SUID,这就好像线程头部。告诉JVM所有版本中有着同样SUID的都是同一个版本。

3.复制之前版本类的SUID。运行期间这个版本和之前版本是一样的版本。

4.使用类每个版本生成的SUID。如果SUID与新版本的类不同,那么运行期间两个版本是不同的,并且老版本类序列化后的实例并不可以反序列成新的类的实例。

实序列化的作用是能转化成Byte流,然后又能反序列化成原始的类。能在网络进行传输,也可以保存在磁盘中,有了SUID之后,那么如果序列化的类已经保存了在本地中,中途你更改了类后,SUID变了,那么反序列化的时候就不会变成原始的类了,还会抛异常,主要就是用于版本控制。


  • 如何主动请求GC

1.System.gc()方法

调用System.gc()可以主动请求垃圾回收机制,但也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。

良好的编程习惯是:GC调用不宜过多,保持代码健壮(记得将不用的变量置为null、资源使用完后释放),让虚拟机去管理内存。因为System.gc()方法会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

2.finalize()方法

一旦垃圾回收器准备好释放对象占用的存储空间,首先会去调用finalize()方法进行一些必要的清理工作。只有到下一次再进行垃圾回收动作的时候,才会真正释放这个对象所占用的内存空间(所以调用finalize()并不一点会GC)。

另外finalize()函数是在垃圾回收器准备释放对象占用的存储空间的时候被调用的,绝对不能直接调用finalize(),所以应尽量避免用它


  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###

  • ###


谢谢大家阅读,如有帮助,来个喜欢或者关注吧!


本文作者:Anderson/Jerey_Jobs

简书地址 : Anderson大码渣

CSDN地址 : Jerey_Jobs的专栏

github地址 : Jerey_Jobs