当前位置:   article > 正文

Java小试牛刀(面试题一)_java serializable 为空时不返回

java serializable 为空时不返回

一.Static(静态)关键字

  Static用于修饰成员(成员变量和成员函数)
  被修饰后的成员具备一下特点:
  1.随着类的加载而加载(既然被静态修饰的数据要被对象所共享,所以它必须在对象创建之前就要加载完成,随着类的加载就已经在内存中
  进行了空间的分配,随着类的消失而消失)
  2.优先于对象存在  
  3.被所有对象所共享 
  4.可以直接被类名调用(没有对象也可以被调用,只有类可以完成这个调用动作,所以静态成员多了一种调用方式   类名.静态成员)
  5.被静态修饰的成员变量和成员函数在方法区中(以便共享)
  使用注意:
  1.静态方法只能访问静态成员,不能调用非静态成员。(因为非静态的成员只有在创建对象时才在内存中的存在,而静态的方法在对象
  创建之前就要出现在内存中)先来后到  前面不能访问后面的  后面可以访问前面的非静态方法可以调用静态成员,也可以调用非静态成员。
  2.静态方法中不可以写this,super关键字    this代表对象,而静态方法执行时还没有对象呢。
  3.主函数是静态的
  当产生很多对象的时候,每个对象都是属性的封装体
  就拿这个例子来说,当创建很多个不同的人对象的时候,姓名name的值是不一样的,但是国家的值都相同,也就意味着生成多少个对象,
  堆内存中就得出现对少个CN(特别浪费空间)相同的值在N多对象中出现,对空间而言就是一种浪费,这个时候就用到了静态(你想把一些
  数据从对象中提取出来,就可以用static关键字静态)这样就可以为所有的对象所共享  就在这个数据前面加上一个静态修饰符static
  注意区分:静态成员变量和非静态成员变量:
  1.非静态成员变量又称为实例变量 静态成员变量又称为类变量
  2.非静态成员变量,随着对象的创建而存在,随着对象的消失而消失静态成员变量,随着类的加载而加载,随着类的消失而消失
  3.非静态成员变量存在于对象堆内存中   静态成员变量存在于方法区中
  4.非静态成员变量只能被对象所调用  静态变量可以被类名调用,也可以被对象调用。
  创建对象之前会加载类,静态初始化块会随着类的加载而加载,对整个类进行初始化,而且子类会先执行顶层父类的静态初始化块后
  才是子类静态初始化块,创建对象时现执行父类行初始化代码块然后执行构造函数,然后执行子类的行初始化代码块之后才是子类的构造函数。
  (构造代码块之所以在构造函数之前运行是因为,构造代码块要对所有对象进行初始化,静态代码块是对类进行初始化)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

二.继承(extends)

  好处:提高了代码的复用性,让类与类之间产生了关系。
       将对象中的共性内容不断的向上抽取就形成了关系,就有了继承,有了子父类提高了代码的复用。
  特点:只能单继承,多继承的机制被java语言改良了。
  单继承:一个类只能有一个父类。一个儿子只能有个父亲
  多继承:一个类可以有多个父类。一个儿子有多个父亲
  Java不直接支持多继承:原因:会产生调用的不确定性。
  Java支持多层继承。这样就出现了继承体系。
  继承中成员函数的特点:当子夫类中出现一模一样的方法时,会发生一个函数的特性:覆盖(复写,重写)override
  覆盖注意事项:
  1,子类覆盖父类时,必须要保证覆盖方法的权限大于等于被覆盖方法的权限
  2,覆盖方法有静态修饰时,静态只能覆盖静态,或者被静态覆盖,在写法上注意这个事项
  3,构造函数
  子类的实例化过程:
     其实在子类的所有构造函数中的第一行,默认都有一条隐式的语句。就是super(),也就是说子类的构造函数默认都会访问
     父类中空参数的构造函数。
  为什么子类的构造函数都要去默认访问父类的构造函数呢?
     因为子类继承了父类,可以访问父类中已有的一些属性。在子类进行实例化的时候必须要为父类中的属性分配空间。并要进行初
     始化,所以必须要访问一次父类的构造函数,看看父类是如何对其属性 进行初始化的。所以子类在实例化对象时,必须要先看
     父类的初始化过程。
  结论:父类的构造函数既可以对本类对象进行初始化,也可以对子类对象进行初始化。
  注意:如果父类中没有空参的构造函数,子类的构造函数中必须手动用super指定要访问的父类中的构造函数。或者用this()来访
  问本类中的构造函数。
  this和super调用构造函数只能定义在构造函数的第一行,不能同时出现,子类中的构造函数中要么只有this(),要么只有super(),
  不允许同时存在。
  为什么要定义在第一行?因为初始化的动作要先完成,不初始化,用不了。
  继承弊端:打破了封装性
  解决方法,关键字final
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

三.final关键字的特点:

 1.final是一个修饰符,既可以修饰类,又可以修饰方法,开可以修饰变量(什么便来给你都包含)。
 2.final修饰的类不可以被继承。
 3.final修饰的方法不可以被覆盖。
 4.final修饰的变量是一个常量,只能赋值一次。
 为了将固定的一些数据方便使用,会给这些数据起一个容易阅读的名称,为了防止该名称存储的数据改变,用final修饰一般被final
 修饰的变量名称都是大写字母组成,如果有多个单词每个单词之间用下划线分割。在开发时,一旦程序中出现固定的数据,一定要将
 其用一个容易阅读的名称存储,并用final修饰。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

四.一个对象在内存中的产生过程:

 1.将该对象所需的类文件加载进内存
 2.在内存中进行方法区的空间分配
 3.通过new在对内存中开辟空间
 4.对象中的属性进行默认初始化
 5.调用与之对应的构造函数进行初始化
 6.通过构造函数中的super调用父类中的构造函数初始化
 7.对象中的属性进行显示初始化
 8.构造代码块初始化
 9.该构造函数内部自定义内容进行初始化。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

五.抽象类

当描述事物时,没有足够的信息对该事物进行描述,那么该描述对应的类就是一个抽象类。
抽象类的特点:
1.没有方法体的方法是抽象方法,一定定义在抽象类中。
2.抽象类和抽象方法必须用abstract关键字修饰。
3.抽象类不可以被实例化。为啥呢?因为调用抽象方法没有意义
4.抽象类必须由其子类覆盖掉所有的抽象方法后,其子类才可以进行实例化,否则该子类还是一个抽象类。
细节问题
1.抽象类一定是个父类?是
2.抽象类是否有构造函数?有,因为是给子类对象提供初始化动作的。
3.抽象类是否可以不定义抽象方法?可以的,就是不让该类创建对象。这种情况在java的体系中就有存在,windowAdapter
4.抽象关键字不能和那些关键字共存?
  非法的修饰符组合final:private:static:说明不需要用对象调用
一般类和抽象类有什么异同呢?
相同之处:一般类和抽象类都用于描述事物,里面都可以定义属性和行为,以及构造函数。
不同之处:
一般类中不可以定义抽象函数,抽象类可以,
一般类可以被实例化,抽象类不可以。
一般类可以被继承,也可以不被继承,
抽象类一定要被继承,需要其子类覆盖所有的抽象方法子类才可以被实例化。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

六.接口:

当一个抽象类中全都是抽象方法的时候,这时,可以将抽象类定义成接口。
接口是一个特殊的抽象类,意味着抽象类中的方法都是抽象方法。
接口中不能定义变量,只能定义常量。
接口中的成员都有固定的修饰符。(接口中不存在着构造函数)
1.常量:   有固定的修饰符  public  static  final  全局常量。
2.抽象方法 有固定的修饰符  public  abstract  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

七.普通集合框架

集合类的由来:对象用于封装数据,对象多了需要存储,如果对象的个数不确定。就使用集合容器进行存储。
集合特点:
1.用于存储对象的容器。
2.集合的长度是可变的。
3.集合中不可以存储基本数据类型值。
集合容器因为内部的数据结构不同,有多种具体容器。不断的向上抽取,就形成了集合框架。
框架的顶层Collection接口:
Collection常见的方法:
 1.添加:
 boolean add(Object obj);
 boolean addAll(Collection coll);
 2.删除:
 boolean remove(Object obj);
 boolean removeAll(Collection coll);
 void clear();清空
3.判断:
 boolean contains(Object obj);
 boolean containsAll(Collection coll);
 boolean isEmpty();判断集合是否有元素
 4.获取:
 int size();
 Iterator iterator();迭代器    取出集合中元素的方式  该对象必须依赖于具体的容器,因为每一个容器的数据结构都不一样 
 所以该迭代器对象是在容器中进行内部实现的。对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的
 对象即可  也就是iterator方法。Iterator接口就是对所有的Collection容器进行元素取出的公共接口。其实就是抓娃娃机中的夹子  
 5.其他:
 boolean retainAll(Collection coll);取交集。
 Object[] toArray();将集合转成数组。
 Collection
    |--List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
    |--Set: 元素不能重复,无序。(有可能会有序)
 List:特有的常见方法:  有一个共性特点就是都可以操作角标
   1.添加
   void add(index,element);
   void add(index,collection);
   2.删除
   Object remove(index);
   3.修改
   Object set(index,element);
   4.获取:
   Object get(index);
   int indexOf(object);
   int lastIndexOf(object);
   List subList(from,to);
   List集合是可以完成对元素的增删改查。
   List:
      |--Vector:内部是数组数据结构。是同步的(线程安全)(几乎不用了)  增删查询都很慢 !   百分之百延长
      |--ArrayList:内部是数组数据结构,是不同步的。替代了Vector   查询的速度块   百分之五十延长
      |--LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。
  LinkedList:
     addFirst();
     addLast();
     offerFirst();
     offerLast();
     getFirst();//获取但不移除,如果链表为空,抛出NoSuchElementException
     getLast();
     peekFirst();//获取但不移除,如果链表为空,返回null.
     peekLast();
     removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException
     removeLast();
     pollFirst();//获取并移除,如果链表为空,返回null.
     pollLast();
Set:元素不可以重复,是无序
Set接口中方法和Collection一致
     |--HashSet:内部数据结构是哈希表,是不同步的。
     |--TreeSet:可以对Set集合中的元素进行排序。是不同步的。
     判断元素唯一性的方式:就是根据比较方法的返回值是否是0,是0,就是相同元素。
     TreeSet对元素进行排序的方式一:
      让元素自身具备比较功能,于是就需要实现Comparable接口。覆盖compareTo方法。如果不要按照对象中具备的自然顺序进行排序。
      如果对象中不具备自然顺序。可以使用TreeSet集合的第二种排序方式二:让集合自身具备比较功能,定义一个类实现Comparator
      接口,覆盖compare方法。将该类对象作为参数传递给TreeSet集合的构造函数。
      哈希表确定元素是否相同:
      1.判断的是两个元素的哈希值是否相同。
        如果相同,再判断两个对象的内容是否相同
      2.判断哈希值相同,其实判断的是对象的hashCode的方法,判断内容相同,用的是equals方法
      注意:如果哈希值不同,是不需要判断equals的
      第一次判断: 哈希值相同不一定是同一个元素
      第二次判断:如果哈希值相同之后,再判断内容
 Map:一次添加一对元素。Collection 一次添加一个元素。
 Map也称为双列集合,Collection集合称为单列集合。
 其实Map集合中存储的就是键值对。
 Map集合中必须保证键的唯一性。
 常用方法:
 1.添加
 value put(key,value):返回前一个和key关联的值,如果没有返回null.
 2.删除
 void  clear();清空Map结合
 value remove(key):根据指定的key删除这个键值对。
 3.判断
 boolean containsKey(key);
 boolean containsValue(value);
 boolean isEmpty();
 4.获取
 value get(key):通过键获取值,如果没有该键,返回null.当然可以通过返回null,来判断是否包含只当键。
 int size():获取键值对的个数。
 Map常用的子类:
 |---HashTable:内部结构是哈希表,是同步的。不允许null作为值,不允许null作为键。
     Properties:用来存储键值对型的配置文件信息。可以和IO技术相结合。
 |---HashMap:内部结构是哈希表,不是同步的。允许null作为值,允许null作为键。
 |---TreeMap:内部结构是二叉树,不是同步的,可以对Map集合中的键进行排序。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

八.泛型

jdk1.5出现的安全机制
好处
1.将运行 时期的安全问题ClassCastException转到了编译时期
2.避免了强制转换的麻烦.
3.编译时期的安全技术
<>:什么时候用?当操作引用数据类型不确定的时候.就使用<>.将要操作的引用数据类型传入即可。其实<>就是一个用于接
收具体引用数据类型的参数范围.
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型.
泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全.
擦除补偿:运行时,会将泛型去掉,生成的class文件中是不带着泛型的,这个称为泛型的擦除.
为什么擦除,因为为了兼容运行时的类加载器.只在编译器中加入了泛型技术
泛型的补偿:在运行时,通过获取元素的类型进行转换动作.不用使用者再强制转换了.
泛型的通配符: ? 未知类型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

九.集合的一些技巧(列举常用集合)

 需要唯一吗?
 需要:set
       需要指定顺序吗?
       需要:TreeSet
       不需要:HashSet
       不需要但是想要一个和存储一致的顺序(有序):LinkedHashSet
 不需要:List
       需要频繁增删吗?
       需要:LinedList
       不需要:ArrayList
 如何记住每一个容器的结构和所属体系呢?看名字!
 List
    |----ArrayList
    |----LinkedList
 Set
    |----HashSet
    |----TreeSet
 后缀名就是该集合所属的体系
 前缀名就是该集合的数据结构
 看到Array就要想到数组,就要想到查询快,有角标
 看到Link就要想到链表,就要想到增删块,就要想到add,get ,remove+first last方法
 看到Hash就要想到Hash表,就要想到唯一性,就要想到元素需要覆盖hashCode方法和equals方法
 看到Tree就要想到二叉树,就要想到排序,就要想到两个接口Comparable,Comparator。
 Comparator和Comparable的区别?
 Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以
 有多个 comparator 来定义
 对象的顺序。
 而且通常这些常用的集合容器都是不同步的。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

十.volatile变量

  java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile
  类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量
  不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
  • 1
  • 2
  • 3

在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

十一.序列化与反序列化

  序列化:把Java对象转换为字节序列的过程。
  反序列化:把字节序列恢复为Java对象的过程。
  Java对象是在JVM中生成的,如果需要远程传输或保存到硬盘上,就需要将Java对象转换成可传输的文件流。
  三种方式
  1.利用Java的序列化功能序列成字节(字节流)也就是接下来要讲的。一般是需要加密传输时才用。
  2.将对象包装成JSON字符串(字符流)
  3.protoBuf工具(二进制)
  实现方式
  实现了如下两个接口之一的类的对象才能被序列化:
  1).Serializable 
  2).Externalizable
  序列化:ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节
  序列写到一个目标输出流中。
  反序列化:ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对
  象,并将其返回。
  注:使用writeObject() 和readObject()方法的对象必须已经被序列化
  关于serialVersionUID
  如果serialVersionUID没有显式生成,系统就会自动生成一个。此时,如果在序列化后我们将该类作添加或减少一个字段等的操作,系统
  在反序列化时会重新生成一个serialVersionUID然后去和已经序列化的对象进行比较,就会报序列号版本不一致的错误。为了避免这种
  问题, 一般系统都会要求实现serialiable接口的类显式的生明一个serialVersionUID。
  所以显式定义serialVersionUID有如下两种用途:
  1.希望类的不同版本对序列化兼容时,需要确保类的不同版本具有相同的serialVersionUID;
  2.不希望类的不同版本对序列化兼容时,需要确保类的不同版本具有不同的serialVersionUID。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

十二.下面是一些面试问题

1.Java 中 sleep 方法和 wait 方法的区别?

  虽然两者都是用来暂停当前运行的线程,但是sleep()实际上只是短暂停顿,因为不会释放锁,而wait表示条件等待,需要释放锁,
  其他等待的线程才能在满足条件时获取到该锁。
  • 1
  • 2

2.解释Java堆空间及GC?

  当通过java命令启动java进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从堆空间中分配内存,
  GC是JVM内部的有一个进程,回收无效对象的内存用于将来的分配。
  • 1
  • 2

3.Java中堆和栈有什么区别?

  VM中堆和栈属于不同的内存区域,使用目的也不同,栈常用于保存方法帧和局部变量,而对象总是在堆中分配,栈比较小,不在多线程中共享,
  而堆被整个JVM的所有线程共享。
  • 1
  • 2

4.Thread类中的start()和run()方法有什么区别?

  start()方法被用来启动新创建的线程,而且,start()内部调用了run()方法,这和直接调用run()方法的效果不一样,
  当你调用run()方法的时候,只会是咋线程中调用,没有新的线程启动,start()方法才会启动新线程。
  • 1
  • 2

5.”static”关键字是什么意思?Java中是否可以覆盖(override) 一个private或者是static的方法?

 “static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例的情况下被访问。
 Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。
 static方法跟类的任何实例都不相关,所以概念上不适用。
  • 1
  • 2
  • 3

6.是否可以在static环境中访问非static变量?

不可以。static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。
如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
  • 1
  • 2

7.Java支持的数据类型有哪些?什么是自动拆装箱?

Java支持的基本数据类型有:byte   short  int  long   float   double   boolean   char
自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把int转化成Integer。反之就是自动拆箱。
  • 1
  • 2

8.Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

方法覆盖是说子类重新实现父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。
方法重载发生在同一个类里面,两个或者是多个方法的方法名相同但是参数列表不同。
  • 1
  • 2

9.Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?

当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。
在程序员没有给类提供构造函数的情况下,Java编译器会为这个类创建一个默认的构造函数。
Java中构造函数重载和方法重载很相似。可以为一个类创建多个构造函数。每一个构造函数必须有它自己唯一的参数列表。
Java不支持像C++那样的复制构造函数,这个不同点是因为如果你不自己写构造函数的情况下,Java不会创建默认的复制构造函数。
  • 1
  • 2
  • 3
  • 4

10.抽象类和接口的区别是什么?

Java支持创建抽象类和接口。它们的区别在于:
接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
类可以实现很多个接口,但是只能继承一个抽象类
类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,在这种情况下,类也必须得声明成是抽象的。
抽象类在实现接口时,可以不实现接口里面的方法。
Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
Java接口中的成员方法默认是public的。抽象类的成员方法可以是private,protected或者是public。
接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

11.什么是值传递?什么是引用传递?

对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。
对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。
  • 1
  • 2

12.进程和线程的区别是什么?

进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。
  • 1

13.创建线程有几种不同的方式?你喜欢哪一种?为什么?

创建线程有以下几种方式:继承Thread类,实现Runnable接口
应用程序可以使用Executor框架来创建线程池
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在已经继承了别的类的情况下,这需要多继承(而Java不支持多继承),
只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。
  • 1
  • 2
  • 3
  • 4

14.解释一下线程的几种可用状态

线程可以处于以下几种状态:
就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):程序正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取锁。
死亡(Dead):线程完成了执行。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

15.同步方法和同步代码块的区别是什么?

同步方法就是在方法前加关键字synchronized,然后被同步的方法一次只能有一个线程进入,其他线程等待。
而同步代码块则是在方法内部使用大括号使得一个代码块得到同步。同步块会有一个锁定的“对象”。同步代码块的同步范围更加准确。
  • 1
  • 2

16.在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

监视器和锁在Java虚拟机中是一起使用的。监视器监视同步代码块,确保一次只有一个线程执行同步代码块。
每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。
  • 1
  • 2

17.什么是死锁(deadlock)?

两个线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个线程都陷入了无限的等待中。
  • 1

18.如何确保N个线程可以访问N个资源同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。
因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
  • 1
  • 2

19.为什么集合类没有实现Cloneable和Serializable接口?

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
  • 1

20.什么是迭代器(Iterator)?

Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。
迭代器可以在迭代的过程中删除底层集合的元素。
  • 1
  • 2

21.Java中的HashMap的工作原理是什么?

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向
集合添加元素和从集合检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。
如果key已经存在了,value会被更新成新值。
  • 1
  • 2
  • 3

22.hashCode()和equals()方法的重要性体现在什么地方?

Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的
实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。
所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。
  • 1
  • 2
  • 3

23.Comparable和Comparator接口是干什么的?列出它们的区别。

Java提供了只包含一个compareTo()方法的Comparable接口。这个方法可以个给两个对象排序。具体来说,它返回负数,0,正数来表明
输入对象小于,等于,大于已经存在的对象。Java提供了包含compare()和equals()两个方法的Comparator接口。compare()方法用来给
两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。equals()方法需要一个对象作为参数,它用来决
定输入参数是否和Comparator相等。只有当输入参数也是一个Comparator并且输入参数和当前Comparator的排序结果是相同的时候,
这个方法才返回true。
  • 1
  • 2
  • 3
  • 4
  • 5

24.什么是Java优先级队列(Priority Queue)?

PriorityQueue是一个基于优先级堆的无界队列,它的元素是按照自然顺序(natural order)排序的。在创建的时候,我们可以给它提供
一个负责给元素排序的比较器。PriorityQueue不允许null值,因为他们没有自然顺序,或者说他们没有任何的相关联的比较器。最后,
PriorityQueue不是线程安全的,入队和出队的时间复杂度是O(log(n))。
  • 1
  • 2
  • 3

25.Java集合类框架的最佳实践有哪些?

根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。
有些集合类允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以设置初始容量来避免重新计算hash值或者是扩容。
为了类型安全,可读性和健壮性的原因总是要使用泛型。同时,使用泛型还可以避免运行时的ClassCastException。
使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。
编程的时候接口优于实现。
底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

26.Enumeration接口和Iterator接口的区别有哪些?

Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,因为其他线程不能够修改正在被
iterator遍历的集合里面的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。
  • 1
  • 2

27.HashSet和TreeSet有什么区别?

HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。
TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)。
  • 1
  • 2

28.Java中垃圾回收(GC)有什么目的?什么时候进行垃圾回收?

垃圾回收(GC)的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。
  • 1

29.System.gc()和Runtime.gc()会做什么事情?

这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。
  • 1

30.finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?

在释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法。一般建议在该方法中释放对象持有的资源。
  • 1

31.如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

不会,在下一个垃圾回收周期中,这个对象将是可被回收的。
  • 1

32.Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系
统也就是垃圾收集器回收。堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。
死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。
  • 1
  • 2
  • 3

33.串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?

吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处
理器上需要大概100M左右的内 存)就足够了。
  • 1
  • 2

34.在Java中,对象什么时候可以被垃圾回收?

当对象对当前使用这个对象的应用程序变得不可触及的时候,这个对象就可以被回收了。
  • 1

35.JVM的永久代中会发生垃圾回收么?

垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出
信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。
  • 1
  • 2

36.Java中的两种异常类型是什么?他们有什么区别?

Java中有两种异常:受检查的(checked)异常和不受检查的(unchecked)异常。不受检查的异常不需要在方法或者是构造函数上声明,
就算方法或者是构造函数的执行可能会抛出这样的异常。而且不受检查的异常可以传播到方法或者是构造函数的外面。相反,受检查
的异常必须要用throws语句在方法或者是构造函数上声明。
  • 1
  • 2
  • 3

37.一个类是由哪些变量构成的?

本地变量:在方法体,构造体内部定义的变量,在方法结束的时候就被摧毁
实例变量:在类里但是不在方法里,在类被载入的时候被实例化
类变量:在类里但是不在方法里,加了 static 关键字,也可以叫做静态变量
  • 1
  • 2
  • 3

38.静态变量和实例变量的区别?

在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:
实例变量属于某个对象的属性,必须创建了实例对象,才能使用这个实例变量。
静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就可以被使用了。
总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来使用。
  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/543037
推荐阅读
相关标签
  

闽ICP备14008679号