Java经典面试题,java面试题及答案整理

Java经典面试题,java面试题及答案整理

Java基础篇

Java有什么特点? 在其中不需要一次执行很多语句,可以在其中执行很多语句。 类和面向对象的编程语言。 的独立性:支持一次编写并在各处执行的独立编程语言。 这意味着编译的代码可以在所有支持Java的平台上运行。

Java的特性Java的特性有以下几点

很简单。 Java使工作更简单,使您可以关注主要业务逻辑,而无需担心指针、运算符过载和内存回收等与主要业务无关的功能。 便携性,Java与平台无关。 这意味着在一个平台上创建的APP应用程序可以方便地移植到另一个平台。 安全,编译后所有代码都转换成字节码,人类无法读取。 支持开发无病毒、无篡改的系统/APP应用程序。 动态。 能够适应不断变化的环境,支持动态内存分配,从而减少内存浪费,提高APP传输性能。 分布式,Java提供的功能有助于创建分布式APP应用程序。 远程方法调用允许程序通过网络调用另一个程序的方法以获取输出。 可以通过从internet上的任何计算机调用方法来访问文件。 这是革命性的特征之一,对今天的互联网非常重要。 坚固性,Java具有强大的内存管理功能,有助于在编译和运行时检查代码并消除错误。 高性能,Java最黑的技术是字节码编程,编译了Java代码的字节码可以简单地转换成本地机器代码。 用JIT实时编译器实现高性能。 描述: Java编译成字节码,并由Java运行时环境解释。 多线程,Java支持包含一组同步原语的多个执行线程。 这使Java使用流水线模型实现线程安全的线程编程变得更容易。 要解释并真正理解传价和传参考的区别,请参阅本文。 https://zikaocs/question/31203609

简单地理解的话

传递值是指在调用函数时将实际参数复制到函数中。 这样,修改函数传递的格式参数不会影响实际参数

引用传递是在调用函数时将对象的地址直接传递给函数,如果对表单参数进行更改,则会影响实际参数的值。

==和equals的区别是什么?==是Java的操作符,有两种比较方法

对于基本数据类型,==确定两边的值是否等于publicclassdoublecompareandequals { person person1=new person ( 24,\’ boy \’ )。 人员人员2=new人员( 24,\’ girl \’; int c=10; private void doubleCompare ( ) { int a=10; int b=10; system.out.println(a==b ); system.out.println(a==c ); system.out.println ( person1. getid (==person2. getid ) ); }}对于引用类型,==确定两侧的引用是否相等,即两个对象是否指向同一个内存区域。 privatevoidequals ( ( system.out.println ) person1.getname ).equals ( person2. getname ) ); }equals是由Java中所有对象的父类或Object类定义的方法。 只能比较对象,并指示引用的两个值是否相等。 所以,请记住==比较的不是引用是否相等,而是比较equals的是值。 这个需要区别开来说。

使用equals作为对象之间的比较具有以下特性

自否认:对于非空引用x,x.equals(x )必须返回true。 对称性:对于非空引用x和引用y,如果x.equals为true,则y.equals也为true。 可传递性:非空引用的值有三个值: x、y和z。 如果x.equals(y )返回true,y.equals(z ) z返回true,则x.equals ( z ) z )也必须返回true。 完整性:对于非空引用x和y,如果x.equals(y )相等,则它们必须始终相等。 非null :如果是非null引用的值x,则x.equals(null )必须返回false。 String中的equals如何被重写的String表示Java字符串。 String类很特殊,整个类都用final限定。 这意味着String不会被任何类继承。 更改字符串的所有方法都是创建新字符串。

equals方法是Object类定义的方法,Object是所有类的父类。 当然,也包括字符串。 String重写了equals方法。 看看是怎么改写的

首先,确定要比较的两个字符串的引用是否相等。 引用相等时直接返回true,不相等时继续下一个判断,然后判断比较对象是否为String的实例,否则直接返回false,否则比较两个字符串的长度是否相等,不想等待长度接下来是其流程图

在这里再提出一次提示,你可能会怀疑是什么时候。

if(this==anobject({returntrue; }该判决书如何才能返回true? 因为是字符串。 字符串被比较的不仅仅是堆区域吗? 突然看起来好像永远不去,但是忘记了String.intern ( )方法。 不同版本的JDK有不同的概念

在JDK1.7或更高版本中调用intern方法是确定运行时常量池中是否存在指定的字符串,如果没有,则将字符串添加到常量池,然后返回常量池中的对象。

验证过程如下

privatevoidstringoverrideequals ( ( string S1=\’ AAA ) ); stringS2=\’aa\’newstring(\’a ); stringS3=newstring(AAA ); system.out.println(S1.intern ) ).equals(S1 ); system.out.println(S1.intern ) ).equals(S2 ); system.out.println(S3.intern ) ).equals(S1 ); }首先s1.intern.equals(s1 )这无论如何都会返回true。 这是因为,创建s1字符串时,它已经存在于常量池中。 然后,第二条语句返回false。 s1返回常量池中的对象,而s2返回堆中的对象。 第三条语句s3.intern.equals(s1 )返回true。 这是因为,虽然s3对象在堆中创建了对象,但s3的\’ aaa \’返回了常量池中的对象。

为什么要重写equals方法,hashcode方法equals方法和hashcode都是用Object定义的方法,它们经常被一起重写。

equals方法是比较对象大小是否相等的方法,而hashcode方法是确定每个对象的hash值的方法。 如果只重写equals方法而不重写hashcode方法,则会出现两个不同的对象,它们的hashcode也将相等,并且很可能会发生冲突。 例如

String str1=\’通话\’; String str2=\’重地\’; 两个hashcode相等,但equals不相等。

让我们看看hashCode公式的定义

总结起来

在执行Java期间对同一对象调用hashCode方法后,无论调用多少次都必须返回相同的hashCode,但在不同的Java程序中,执行hashCode方法返回的值可能不匹配。 如果两个对象的equals相等,则hashCode必须相同。 如果两个对象的equals不相等,则hashCode也可能相同,因此必须重写hashCode方法。 因为不知道hashCode的基本结构,所以必须重写hashCode方法以为不同的对象生成不同的hashCode值。hashCode通常通过将地址转换为整数来实现。 String s1=new String(\’abc \’ )在内存中创建了一个或两个对象。 String s1是声明String类型而不是对象的s1变量。 如果使用new关键字,则会在堆中创建一个对象。 另一个对象是abc,它是在常量池中创建的,因此总共会创建两个对象。 如果常数池中已经存在abc,则将创建对象。

有关详细信息,请参阅笔者另一篇文章中的String、StringBuffer和StringBuilde

字符串为什么不变,jdk源代码中的字符串是如何定义的,为什么是这样设计的? 首先,让我们了解一下什么是不变对象。 不可变对象是指一旦创建,就无法修改对象的内部状态。 这是什么意思? 也就是说,不变对象需要遵守以下原则

不可变对象的内部属性全部为final,不可变对象的内部属性全部为private,不可变对象不能提供修正内部状态的方法,setter方法也不行,不可变对象不能继承或扩展与其问String为什么不变,不如问一下如何设计String不变。

String类是独立于Java基本数据类型而存在的对象。 String可以理解为字符串的集合。 String设计为final,在创建String对象后,无法更改其值。 更改字符串值的方法是重新生成字符串。 创建String对象后,它存在于作为方法区域一部分的运行时常量池中,并在JDK1.7之后移动到堆中。

不变对象不是真的不变的,可以通过反射修改其内部的属性和值,但一般不会这样做。

静态关键词是用来做什么的? 你的理解static是Java中非常重要的关键词,static表示的概念是静态的,Java中static主要是

修饰变量、静态修饰变量也称为静态变量、类变量,类变量是类的所有物,每个类只有一个静态变量,静态修饰变量位于方法区域; 静态限定的变量可以直接通过类名.变量名访问,而不需要在实例化类中重用。 方法,限定静态的方法称为静态方法,静态方法可以直接在类名.方法名中使用。 不能在静态方法内部使用非静态属性和方法static来限定代码块。 有两种主要类型:直接为类定义、使用static{}、定义静态内部类和使用staticclassasic。static可以用于静态引导包,通过使用import static xxx 在这种方式下,静态可以与单实例模式一起使用,一般不建议通过双检查锁来实现线程安全的单实例模式。 详情请参考这篇文章。 static还能住我吗?

final关键词是用来做什么的? 谈谈你的理解吧。 final是Java的关键词,它表示不变的意思。 在Java中,final主要是

限定类,final限定的类不能继承。 不能继承意味着不能使用extends来继承final限定的类。 限定变量、final限定的变量有两种含义:不可改写、不可改写。对于基本数据类型来说,final限定的变量不能更改其值,final限定的对象不能更改对对象的引用由final限定的变量在某种程度上起到了不变的效果,因此可以用于保护只读数据。 它有助于减少额外的同步开销,特别是在并发编程中,因为不能显式地为final变量赋值。 修饰方法、final修饰的方法无法改写。 final修饰符和Java程序的性能优化并不能必然联系抽象类和接口之间的差异。 所有抽象类和接口都是Java关键字,抽象类和接口都允许定义方法,不需要以具体方式实现。 抽象类和接口都允许继承,广泛应用于JDK和框架源代码,实现多态性和不同的设计模式。

不同之处在于

抽象层次的差异:类、抽象类、接口其实是三种不同的抽象层次,抽象度依次为接口抽象类。 接口只允许定义方法,不允许实现方法,抽象类可以定义和实现方法,而类只允许实现方法。 我说的方法的定义是方法后面出现{}不允许使用的关键字的差异。 类用class表示。 抽象类用abstract class表示; 接口使用接口表示变量。 在接口中定义的变量只是公共静态常数,抽象类中的变量是常规变量。 重写和过载的区别在Java中,重写和过载是对同一方法的不同表达。 以下,对改写和过载进行简单的区分

子代和父代关系不同,重写是对子代和父代的不同表达,而过载是同一个类中的不同表达; 概念不同,子类重写父类的方式一般用@override表示; 重写的方法必须与方法的声明和参数的类型、顺序与父类完全匹配。 重载是针对同一个类内的概念的,要重载的方法必须满足方法参数的顺序、参数的个数、参数类型中任意一个不同即可这一要求。 byte的可取范围是多少? 如何计算的byte的取值范围在-128 – 127之间,共有256位。 如果一个byte类型在计算机中占用一个字节,则为8位。 因此,最大值为2^8=1111 1111。

Java用补数表示二进制数。 补数的最高位表示符号位,最高位用0表示正数,最高位用1表示负数。 正数补数本身。 因为最高位是符号位,所以正数表示0111 1111,即127。 最大负数为1111 1111,其中涉及两个0,一个为0,一个为-0,0分类为正数,即0,- 0分类为负数,即-128,因此byte的范围为-128 – 127。

HashMap和HashTable的区别是一样的

HashMap和HashTable基于散列表实现,其中每个元素都是一个密钥-值键和值对,HashMap和HashTable实现Map、Cloneable和Serializable接口

不同点

父类差异: HashMap继承AbstractMap类,而HashTable继承Dictionary类的空值。 HashMap允许空key和值,HashTable不允许空key和值。 HashMap将空密钥视为普通密钥。 不允许重复空密钥。

线程安全: HashMap不是线程安全的。 如果多个外部操作同时更改HashMap的数据结构,例如add或delete,则必须执行同步操作。 只更改key或value不是更改数据结构的操作。 您可以选择要构建线程安全的地图,例如Collections.synchronizedMap或ConcurrentHashMap。 HashTable本身是线程安全的容器。 性能方面: HashMap和HashTable均基于单链表,而HashMap可以通过执行put或get操作达到常数时间的性能; 另一方面,HashTable的put操作和get操作由于被同步锁定,所以效率很低。

初始容量差异: HashTable的初始长度为11,然后每个扩展容量变为以前的2n 1,HashMap的初始长度为16,然后每个扩展变为原来的两倍。 如果在创建时指定了初始容量,HashTable将保留指定的大小,而HashMap将扩展为2的幂大小。 HashMap和HashSet的区别HashSet由AbstractSet接口继承,实现了Set、Cloneable、java.io.Serializable接口。 HashSet不允许集合中的重复值。 HashSet的底边实际上是HashMap,对HashSet的所有操作实际上是对HashMap的操作。 所以HashSet不能保证集合的顺序,也不是线程安全的容器。

在HashMap的基础结构JDK1.7中,HashMap采用了位桶链表的实现。 这意味着,即使使用链表处理冲突,具有相同散列值的链表也会存储在一个数组中。 但是,当一个桶里的元素很多时,也就是说hash值相等的元素很多时,用key值依次查找效率很低。

因此,与JDK 1.7相比,如果对基础结构进行了一些更改,并且每个桶中的元素大于8,JDK 1.8将变为红黑树以优化查询效率。

我思考了几天关于hashMap的长度为什么是2的幂的问题。 以前和小组的伙伴们讨论每天的问题时,我问了为什么length%Hash==(n-1 ) Hash。 假设它们相等,是length长度的2的幂。 而且,Length还不是2的乘方吗? 我回答说。 其实我不明白因果关系,因为HashMap的长度是2的乘方,所以用余数来判断桶里的下标。 如果length的长度不是2的乘方,伙伴们可以举个例子

例如在长度为9的情况下,3(9-1)=0,2 )9-1)=0,都成为0,发生了碰撞;

这将增加HashMap碰撞的概率。

HashMap多线程操作导致的死循环问题HashMap不是线程安全容器。 对于高并发性场景,必须使用ConcurrentHashMap。 在多线程场景中使用HashMap会导致死循环问题,问题发生在rehash,即

do { EntryK,V next=e.next; //–假设线程运行到现在为止,intI=indexfor(e.hash,newCapacity )已计划挂起。 e.next=newTable[i]; newTable[i]=e; e=next; }while(e!=空; 这是JDK1.7的rehash代码片段,在并发场景中形成循环。

JDK1.8也会引起死循环问题。

HashMap线程安全的实现有哪些? 由于HashMap不是线程安全容器,因此建议在并发场景中使用ConcurrentHashMap,或者通过线程安全HashMap使用Collections包下的线程安全容器例如

collections.synchronized map ( new hashmap ) ); 也可以使用HashTable。 这也是一个线程安全容器,基于密钥值存储。 经常用HashMap和HashTable进行比较是因为HashTable的数据结构与HashMap相同。

上面效率最高的是ConcurrentHashMap。

HashMap put的步骤说明如何首先使用hash函数计算key,然后执行真正的插入方法

finalvputval(inthash,K key,V value,boolean onlyIfAbsent,boolean evict ) { NodeK,V[] tab; NodeK,V p; int n,I; 如果//table为null或没有为table分配内存,则resize一次为if((tab=table )==null|| ( n=tab.length )==0( n=) ) 该( n – 1 ) hash才是表中的真正散列if ) ( p=tab ( I=( n-1 ) hash ) ) null ) tab ) I )=newnode ) hash,key //空的else { NodeK k; //计算表的这个真正的散列值与要插入的key.hash相比是if(p.hash==hash ) () k=p.key )==key||(key!=nullkey.equals(k ) ) ( e=p; //如果不同,并且当前节点已经在TreeNode中存储了elseif(pinstanceofTreeNode ) /红黑树的方式e=) ( treenode,v ) p ).puttreeval ),this ; 在binCount ( ) if ) ( e=p.next==null ) /末尾插入p.next=newnode ) hash、key、value、null; //添加节点后,当节点数达到阈值时,转至treeifyBin ( ),再次进入if ( bincount=tree ify _ threshold-1 )//-1 for1streeifybin ( tab ) 黑; (//找到相同hash、key的节点后,直接循环if(e.hash==hash ) )=e.key )|) key!=nullkey.equals(k ) ) ( break; //更新p指向下一个节点=E; } } //map包含旧值,返回旧值if(e )!=null (//existingmappingforkeyvoldvalue=e.value; if (! olyifabsent|||old value==null ( e.value=value; afternodeaccess(e ); return oldValue; } } //map调整次数1 modCount; //键值对的数量达到阈值,扩展if(sizethreshold ) resize ); 自适应身份验证( evict; 返回空值; }HashMap put方法的核心是putval方法,其插入过程如下

首先判断是否是HashMap中新构建的,如果是,则首先进行resize,判断HashMap中是否已经存在下一个要插入的元素,如果不存在,则直接生成并存储新的k-v节点,是否需要扩展如果要插入的元素已经存在,则表示发生了冲突,它将转换为链表或红黑树以解决冲突。 首先判断链表的hash、key是否相等,如果相等,则将旧值替换为新值。 如果节点是TreeNode类型,则直接由红黑树处理,即使hash、key不相等,也不是TreeNode类型。 直接转换为链表处理,进行链表的扫描。 如果链表的next节点为null,则确定是否转换为红黑色树。 如果不转换,则在扫描过程中发现key完全相等的节点时,用新节点替换旧节点ConcurrentHashMap的基础实现ConcurrentHashMap是线程安全的Map,是高并发情形下的首选

Integer缓存池Integer缓存池或IntegerCache是Integer的静态内部类。

默认值用于缓存-128 – 127之间的数字。 如果有-128 – 127之间的数字,则不使用new Integer创建对象,而是直接从缓存池中提取。 此操作有助于减少堆中对象的分配,并提高程序的执行效率。

例如,创建Integer a=24实际上就是调用Integer的valueOf,然后通过反编译得出这个结论

接下来,我们来看看valueOf方法

如果位于指定的缓存池范围内,则直接返回缓存值,而不创建新的Integer对象。

可以使用XX:AutoBoxCacheMax指定缓存的大小。 初始化虚拟机时,Java.lang.integer.integer cache.high属性设置并保存为sun.misc.VM的专用系统属性。

UTF-8和Unicode之间的关系每个国家都有自己的字符编码,因此Unicode的发展旨在创建映射当前大多数语言字符的新标准。 虽然不需要这些字符中的一些,但是它们对于文本的创建是必不可少的。 Unicode统一所有字符的编码,是一个字符集,即字符集。 字符集只是为所有字符提供唯一的编号,但没有规定如何存储。 不同的字符有不同的存储空间,有些可以用一个字节存储,有些需要2、3、4个字节。

UTF-8是一种可以解码文本字符的许多方法,而且很长。 UTF-8表示一组8位表示Unicode字符格式,并使用1 – 4字节来表示字符。

u 0000~u007f:0 xxxxxxxxx u 0080~u07ff:110 xxxxxxxx 10 XXXXXX 0800~ufff:1110 XXXXXX 10 xxxxxxxx 10 xxxxxxxx 10 100000~u11fff:10 它向后兼容ASCII,可以像UTF-32一样包含所有Unicode字符,从而有效减少存储传输过程中的空间占用。

项目在UTF-8环境中,char c=\’中\’,可以合法吗? 由于Unicode代码使用双字节代码,因此UTF-8是一种Unicode实现,它使用可变长度的字符集来编写代码,而char c=\’中\’是双字节代码,因此可以保存。 是合法的。

关于由Arrays.asList得到的List,应该注意到用于将数组转换为List数组的Arrays.asList是Array的静态方法,需要注意以下几点

Arrays.asList转换完成后,List将无法再修改结构化。 什么是结构化修改? 不能再执行增加或减少List元素的操作了。 publicstaticvoidmain ( string ( args ) integer ) ) { 1,2,3,4 }; listintegetlist=arrays.as list ( integer ); integetlist.add(5; }结果直接抛出

查看exceptioninthread \’ main \’ Java.lang.unsupportedoperationexception源代码就知道问题了

//这是java.util.Arrays的内部类,Java.util.arraylistprivatestaticclassarraylisteeextendsabstractlimplementsrandomacccccom 这意味着,如果继承的子类没有重写这些方法,则子类的实例调用这些方法将直接抛出异常。

AbstractList中方法的定义如下所示。 知道具体抛出的例外。

公共void add ( intindex,E element ) thrownewunsupportedoperationexception ); }公共存储器( intindex ) thrownewunsupportedoperationexception; }公共设置( intindex,E element ) thrownewunsupportedoperationexception ); }虽然也抛出了set方法,但由于内部类ArrayList重写了set方法,因此可以修改元素。

Arrays.asList不支持基本类型的转换Java基本数据类型不支持使用Arrays.asList方法进行转换

Collection与Collections的区别Collection和Collections是java.util包下的类

Collection是集合类的父类,是顶级接口,大多数抽象类如AbstractList、AbstractSet继承Collection类,Collection类

Collections是集合类的工具类,Collections提供了一些工具类的基本使用

sort方法对当前集合进行排序,实现Comparable接口的类是用于实现线程安全的容器Collections.synchronizedList,collections.synchechist fill,只能使用一个排序方案,用指定元素替换指定列表中的所有元素。 有很多使用方法。 读者可以翻过来看Collections的源代码。 因为Collections无法实例化,所以所有Collections方法都直接从Collections .方法调用。

你知道故障快速和故障安全吗? fail-fast是Java的快速失败机制,java.util包下的所有集合都是快速失败,快速失败会抛出concurrentmodificationexception异常。 fail-fast这仅用于检测错误,不会恢复错误。 故障快速不一定只在多线程环境中存在。 ArrayList也抛出此异常。 主要原因是modCount不等于expectedModCount。

fail-safe是一种Java安全失败机制,表示在遍历过程中复制原始集合的内容并使用复制的集合进行遍历,而不是直接访问原始集合。 迭代过程中会遍历原始集合的副本,迭代程序不会检测到迭代过程中对原始集合所做的更改,因此不会触发concurrentmodificationexception。 java.util.concurrent包下的容器都安全失败,可以在多线程条件下使用并同时进行修改。

ArrayList、LinkedList、Vector的差异也是很久以前就成为话题的问题

ArrayList、链接的List和Vector都是java.util包下的工具类,它们都实现了List接口。

由于ArrayList的基础是动态数组,并且是基于数组特性进行进化的,因此遍历ArrayList的访问速度非常快,但由于涉及到数组的复制,添加和删除速度会变慢。 ArrayList是非线程安全的容器,在并发情况下会导致问题。 如果要使用线程安全容器,建议使用Collections.synchronizedList; 扩展容量时,ArrayList将增加50%。 由于链接列表的基础是双向链表,因此添加和删除链接列表的速度非常快,只需删除元素并将每个指针指向新元素即可。 但是,链接列表的遍历会变慢。 这是因为一次只需访问一个元素就可以知道以下元素的值。 LinkedList也是非线程安全容器,建议使用collections.synchronizedlistvector向量。 vector是线程安全容器,由于每种方法都粗暴地添加了同步锁定,导致添加删除和遍历效率低下。vector增加容量时,容量会加倍。 Exception和Error有什么区别Exception一般是指异常。 Exception主要分为两种异常。 一种是编译期出现的异常,称为checkedException;另一种是程序运行中出现的异常,称为uncheckedException。 常见的checkedException有IOException,uncheckedException统称为RuntimeException,常见的RuntimeException主要有NullPointerException

错误是指在程序运行过程中发生的错误,通常会导致程序崩溃。 Error通常不可恢复,Error无法捕获。

详细情况请参考这篇文章阅读本Exception和Error,与面试官剥皮就没关系了

String、StringBuilder和StringBuffer有什么区别? 字符串是指Java中的字符串。 String类位于java.lang包下,它由final限定。 String字符串一旦创建就不能更改。 String不能更改。String字符串的基础是使用StringBuilder实现的

StringBuilder位于java.util包下,StringBuilder是未线程的安全容器。 StringBuilder的append方法常用于字符串拼接,其拼接效率比String中号的拼接效率更高。 StringBuilder通常不用于并发环境

StringBuffer位于java.util包下。 StringBuffer是线程安全容器,多线程场景通常使用StringBuffer作为字符串的连接

StringBuilder和StringBuffer都由AbstractStringBuilder类继承,AbstractStringBuilder类实现StringBuffer和StringBuilder的常规操作。

动态代理是基于什么原理的代理,一般分为静态代理和动态代理。 它们都是代理模式的应用,静态代理是在程序运行之前编译的,并且程序知道谁将执行代理方法。

另一方面,动态代理只能在程序运行时确定。 与静态代理相比,动态代理的优点是无需更改每个代理类的方法就可以轻松统一处理代理类的函数。 可以说动态代理是基于反射实现的。 通过反射,可以直接处理类或对象,包括获取类的定义、获取声明的属性和方法、调用方法以及在运行时更改类的定义。

动态代理是在运行时构建代理,动态处理方法调用的结构。 动态代理的安装方法有很多,Java提供的代理被称为JDK动态代理,JDK动态代理是基于类的继承。

int和Integer的区别int和Integer的区别太多了

int是Java的基本数据类型,int表示整数类型,一个int占用4个字节或32位。 int的初始值的缺省值为0,int通过Java内存模型分配给堆栈,int没有方法。 Integer是Java基本数据类型的包装类,Integer是对象,Integer可以进行方法调用。 Integer的默认值为null,Integer在Java内存模型中分配给堆。 int和Integer可以在计算时相互转换。 int – Integer流程称为装箱,Integer – int流程称为开箱。 Integer还提供了IntegerCache,值为-128 – 127的Java提供了什么I/O类型的Java I/O方法

Java I/O包的实现相对简单,但容易出现性能瓶颈,传统的I/O基于同步块。

在JDK 1.4之后提供的NIO (即java.nio包)下,提供了基于channel、Selector和Buffer的抽象,用于构建复用和异步无阻塞I/O程序。

从JDK 1.7开始,NIO得到了进一步的改进,引入了异步无阻塞方法,也称为异步无阻塞方法( AIO )。 用生活中的例子来说明,如果项目经理让手下的员工修复错误,项目经理就不会一直等着员工解决错误。 他一定会在员工解决bug的时候给其他下属分配bug或者做其他事情,等员工解决了bug之后再告诉项目经理bug已经解决了。

谈谈你知道的设计模式思维导图的街道

例如,可以使用全局唯一性或单实例模式。

可以使用策略模式优化过多的if.else .

建立标准模板模型

继承别人的锅,但我不想改变原来的班级适配器模式

使用组合而不是继承

使用装饰器可以制作加糖和奶酪的咖啡

代理人可以用于任何中间商……。

Comparator和Comparable有什么区别? Comparable就像自然排序Comparator,就像在同时存在自定义排序时使用Comparator规则进行比较一样。

某些常见的数据类型在缺省情况下实现了Comparable接口,并实现了compareTo方法。 可以直接使用。

某些自定义类可能需要在不同的情况下实现不同的比较策略。 可以创建新的Comparator接口,并使用特定的Comparator实现进行比较。

Object类一般有哪些方法? Object类是所有对象的父类,包含所有对象都可以使用的方法

用于计算hashCode ( )对象的散列代码equals :用于比较对象之间的值是否相等的toString ) :用于将对象转换为字符串的clone ) :在对象之间复制wait :用于通知所有对象资源已释放的finalize ( ) :用于通知垃圾回收器的getClass ) :有关获取对象类的Java泛型和类型擦除的Java泛型和擦除,请参阅第1篇

是反射的基本原理。 通过反射创建类实例的三种方法是使Java程序具有在运行时反射的能力。 通过反射,可以直接处理类和对象,包括获取类的定义、获取类的属性和方法以及生成方法。

创建类实例的三种方法是

对象. getClass (; Class.forName )创建对象实例(.newInstance ) )方法区分强引用、弱引用、虚引用和幻影引用实际上是合乎逻辑的,但对于虚拟机,主要是对象差异

请按下列步骤总结对象的生命周期

对象将被创建并初始化,在运行时使用,脱离对象的范围,对象将变为不可访问,并被回收到垃圾收集器中。 图中用红色表示的区域表示对象处于强可达的阶段。

JDK1.2介绍了java.lang.ref包。 对象的生命周期包括四个阶段:“强”、“软”、“弱”Weak Reachable和“镜像”

如果只考虑满足垃圾回收条件的对象的话,就只有软件、软件、幻象三种。

软可达:软可达是指我们只能通过软参考进行访问的状态。 可变可达的对象是由SoftReference引用的对象,而且是没有强引用的对象。 软参考用于描述有用但不是必需的对象。 垃圾收集器会尽可能长时间地保留软参考对象,但会在出现OutOfMemoryError之前回收软参考对象。 如果回收软引用对象后仍无法充分分配内存,则直接抛出OutOfMemoryError。 弱可达:弱可达的对象是WeakReference参照的对象。 垃圾收集器可以随时收集弱引用的对象,并且不试图保留弱引用的对象。 幻象可达(幻象可达是PhantomReference引用的对象,与幻象可达没有强、软、弱的引用而相关,而且已经过了finalize,只有幻象的引用指向这个对象时除此之外,还有很强的可到达性和不可到达性两个可到达性判断条件

强可达:一种不可达( unreachable ),在其中创建并初始化了对象,并且所有正在使用的对象都处于强可达状态。 拥有无法传递的对象意味着可以拭除对象。 以下是各种可达性状态的转换图

确定可达性的条件也是确定JVM垃圾回收器如何处理对象的考虑因素的一部分。

的所有对象可访问性引用都是java.lang.ref.Reference的子类,并且具有返回引用对象的get ( )方法。 如果程序或垃圾收集器清除了此引用对象,则此方法返回null。 也就是说,除了幻影参照以外,软参照和弱参照也可以得到对象。 而且,这些对象可以人为拯救,成为强烈的参照。 例如,您只需将this关键字指定给对象,然后将其与引用链上的任何对象重新关联。

final、finally和finalize ( ) )之间的差异可以说是不相关的。 如上所述,final可用于限定类、变量和方法。 请参考上述文件的问题。

finally是一个关键字,常用于try块,用于异常处理。 try .使用finally的代码块类型时,finally部分的代码一定会执行,因此经常在finally方法中用于资源的关闭操作。

JDK1.7建议使用try-with-resources优雅地关闭资源。 这是直接使用try ( )关闭资源即可,不需要写finally关键词。

finalize是Object对象中的一种方法,用于对象的回收方法。 一般不推荐这个方法。 finalize与垃圾回收有关。 在Java 9中,将finalize标记为deprecated,如果没有特殊原因,不要实现finalize方法或期望进行垃圾回收

内部类有哪些分类? 在Java中,一个类的定义可以位于另一个类的定义内部。 这就是内部类。 内部类本身是类的属性,与其他属性的定义方法一致。

内部类的分类一般主要有四种

成员内部类本地内部类匿名内部类静态内部类静态内部类是在类内部定义的静态类。 静态内部类可以访问外部类的所有静态变量,而不是外部类的非静态变量。

成员内部类是在类内部定义并位于成员位置的非静态类,是成员内部类。 成员内部类可以访问外部类的所有变量和方法,包括静态和非静态、专用和公共。

方法中定义的内部类是本地内部类。 在实例方法中定义的本地类可以访问外部类的所有变量和方法,而在静态方法中定义的本地类只能访问外部类的静态变量和方法。

匿名内部类是指未命名的内部类。 除了没有名称外,匿名内部类还具有以下特征:

匿名内部类无法确定继承抽象类或实现接口的匿名内部类

义任何静态成员和静态方法。当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。说出几种常用的异常NullPointerException: 空指针异常NoSuchMethodException:找不到方法IllegalArgumentException:不合法的参数异常IndexOutOfBoundException: 数组下标越界异常IOException:由于文件未找到、未打开或者I/O操作不能进行而引起异常ClassNotFoundException :找不到文件所抛出的异常NumberFormatException: 字符的UTF代码数据格式有错引起异常;InterruptedException: 线程中断抛出的异常静态绑定和动态绑定的区别

一个Java 程序要经过编写、编译、运行三个步骤,其中编写代码不在我们讨论的范围之内,那么我们的重点自然就放在了编译 和 运行这两个阶段,由于编译和运行阶段过程相当繁琐,下面就我的理解来进行解释:

Java 程序从源文件创建到程序运行要经过两大步骤:

1、编译时期是由编译器将源文件编译成字节码的过程

2、字节码文件由Java虚拟机解释执行

绑定

绑定就是一个方法的调用与调用这个方法的类连接在一起的过程被称为绑定。

绑定主要分为两种:

静态绑定 和 动态绑定

绑定的其他叫法

静态绑定 == 前期绑定 == 编译时绑定

动态绑定 == 后期绑定 == 运行时绑定

为了方便区分: 下面统一称呼为静态绑定和动态绑定

静态绑定

在程序运行前,也就是编译时期 JVM 就能够确定方法由谁调用,这种机制称为静态绑定

识别静态绑定的三个关键字以及各自的理解

如果一个方法由 private、static、final 任意一个关键字所修饰,那么这个方法是前期绑定的

构造方法也是前期绑定

private:private 关键字是私有的意思,如果被 private 修饰的方法是无法由本类之外的其他类所调用的,也就是本类所特有的方法,所以也就由编译器识别此方法是属于哪个类的

public class Person { private String talk; private String canTalk(){ return talk; }}class Animal{ public static void main(String[] args) { Person p = new Person(); // private 修饰的方法是Person类独有的,所以Animal类无法访问(动物本来就不能说话)// p.canTalk(); }}

final:final 修饰的方法不能被重写,但是可以由子类进行调用,如果将方法声明为 final 可以有效的关闭动态绑定

public class Fruit { private String fruitName; final String eatingFruit(String name){ System.out.println(\”eating \” + name); return fruitName; }}class Apple extends Fruit{ // 不能重写final方法,eatingFruit方法只属于Fruit类,Apple类无法调用// String eatingFruit(String name){// super.eatingFruit(name);// } String eatingApple(String name){ return super.eatingFruit(name); }}

static: static 修饰的方法比较特殊,不用通过 new 出某个类来调用,由类名.变量名直接调用该方法,这个就很关键了,new 很关键,也可以认为是开启多态的导火索,而由类名.变量名直接调用的话,此时的类名是确定的,并不会产生多态,如下代码:

public class SuperClass { public static void sayHello(){ System.out.println(\”由 superClass 说你好\”); }}public class SubClass extends SuperClass{ public static void sayHello(){ System.out.println(\”由 SubClass 说你好\”); } public static void main(String[] args) { SuperClass.sayHello(); SubClass.sayHello(); }}

SubClass 继承 SuperClass 后,在

是无法重写 sayHello 方法的,也就是说 sayHello() 方法是对子类隐藏的,但是你可以编写自己的 sayHello() 方法,也就是子类 SubClass 的sayHello() 方法,由此可见,方法由 static 关键词所修饰,也是编译时绑定

动态绑定

在运行时根据具体对象的类型进行绑定

除了由 private、final、static 所修饰的方法和构造方法外,JVM 在运行期间决定方法由哪个对象调用的过程称为动态绑定

如果把编译、运行看成一条时间线的话,在运行前必须要进行程序的编译过程,那么在编译期进行的绑定是前期绑定,在程序运行了,发生的绑定就是后期绑定

public class Father { void drinkMilk(){ System.out.println(\”父亲喜欢喝牛奶\”); }}public class Son extends Father{ @Override void drinkMilk() { System.out.println(\”儿子喜欢喝牛奶\”); } public static void main(String[] args) { Father son = new Son(); son.drinkMilk(); }}

Son 类继承 Father 类,并重写了父类的 dringMilk() 方法,在输出结果得出的是儿子喜欢喝牛奶。那么上面的绑定方式是什么呢?

上面的绑定方式称之为动态绑定,因为在你编写 Father son = new Son() 的时候,编译器并不知道 son 对象真正引用的是谁,在程序运行时期才知道,这个 son 是一个 Father 类的对象,但是却指向了 Son 的引用,这种概念称之为多态,那么我们就能够整理出来多态的三个原则:

继承重写父类引用指向子类对象

也就是说,在 Father son = new Son() ,触发了动态绑定机制。

动态绑定的过程

虚拟机提取对象的实际类型的方法表;虚拟机搜索方法签名;调用方法。动态绑定和静态绑定的特点

静态绑定

静态绑定在编译时期触发,那么它的主要特点是

1、编译期触发,能够提早知道代码错误

2、提高程序运行效率

动态绑定

1、使用动态绑定的前提条件能够提高代码的可用性,使代码更加灵活。

2、多态是设计模式的基础,能够降低耦合性。

相关推荐

幼儿保教知识与能力考点归纳:儿童发展理论

一、成熟势力说成熟势力说简称成熟论,主要代表人物是美国心理学家格赛尔(A.Gesel...

自考报名注意事项有哪些,自考报名你应该注意哪些事情?小白必看的书

应该有很多自考的同学都和曾经的我一样,考了三年连自考分为哪几种,自考毕业可以为何所用...