JAVA常见面试题

一.java面向对象

1.面向对象都有哪些特征以及你对这些特征的理解

1)继承:继承是从已有类得到继承信息创建新类的过程.提供继承信息的类被称为父类(超类,基类);得到继承信息的类被称为子类(派生类).继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段.

2)多态性:多态性是指允许不同子类的对象对同一消息做出不同的响应.简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情.多态性分为编译时的多态性和运行时的多态性.如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的。方法重载(ovreload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定).运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1.方法的重写(子类继承父类并重写父类中已有的或抽象方法);2.对象造型(用父类的引用指向子类对象,这样同时的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为).

3)封装:通常认为是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口.面向对象的本质就是将现实世界描绘成一系列完全自治、封装的对象.我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装.可以说,封装就是隐藏一切可以隐藏的东西,只向外界提供最简单的编程接口.

4)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两个方面.抽象只关注对象有哪些行为和属性,并不关注这些行为的细节是什么.

2.访问权限修饰符public、private、protected以及不写(默认)的区别

修饰符当前类同包子类其他包
public
protected×
default××
private×××

3.为什么要用Clone
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的.在java语言中,用简单的赋值语句是不能满足这种需求的要满足这种需求虽然有很多途径,但实现Clone()方法是其中最简单,也是最有效的手段.

4.new一个对象的过程和clone一个对象的过程区别

new操作符的本意是分配内存.程序执行到new操作符时,首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间.分配完内存之后,在调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部使用这个引用操纵这个对象.
clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和原对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完毕之后,clone方法返回,一个新的相同的对象创建,同样可以把这个新对象的引用发布外部.

5.复制对象和复制引用的区别

Person person = new Person(12,"cs");
Person person1 = person;
System.out.println(person);
System.out.println(person1);

当Person person1 = person后,是创建了一个新的对象吗.

1. com.cs.Person@2f9ee1ac
2.com.cs.Person@2f9ee1ac

可以看出,打印的地址是相同的,既然地址都是相同的,那么肯定时同一个对象.person和person1只是引用而已,他们都指向了一个相同的对象Persion(12,“cs”).可以把这种现象叫做引用复制.上面代码执行完成之后,内存中的情景如下所示:
在这里插入图片描述
而下面的代码才是真正的对象

Person person = new person(12,"cs");
Person person1 =(Person)person.clone();
System.out.println(person);
System.out.println(person1);

从打印的结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象,而不是把原来的地址赋给了一个新的引用变量:

com.cs.Person@2f9ee1ac
com.cs.Person@67f1fba0

以上代码执行完成后,内存中的情景如下图所示.
在这里插入图片描述
6.JAVA中有没有goto语句

goto是java的保留字,在目前版本的java中没有使用。根据James Gosling(java之父)编写的《The Java Programming Lanaguage》一书的附录中给出了一个java关键字列表,其中goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语言的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都视为保留字.

7.& 和 && 的区别
&运算符有两种用法:(1)按位与;(2)逻辑与

示例
	7:对应的二进制 0000 0111
	6:对应的二进制 0000 0110
	
那么 7&6
0000 0111
0000 0110
——————
0000 0110
结果为6

&&运算符是短路与运算.逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符合左右两端的布尔值都是true整个表达式的值才是true.

&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算.很多时候我们可能都需要使用&&而不是&,例如在验证用户登陆时判断用户名不是null而且不是null而且不是空字符串,应当写为username != null && !username.equals(""),二者不能交换,更不能使用&运算符,因为第一个条件如果不成立,根本就不能进行字符串的equals比较,否者会产生NullPointerExcepton异常,注意:逻辑运算符
(|)和短路或运算符(||)的差别也是如此.

8.在java中,如何跳出当前的多重循环
在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环.(Java 中支持带标签的break和continue语句,作用有点类似于c和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为他不会让你的程序变得更优雅,很多时候甚至有相反的作用)

int i =0;
int j = 0;
label:while(true){
            //我是第一层循环
            while(true){
                //我是第二层循环
                if(j*i == 81)
                break label; // continue label
                 j++;
            }
           i++;
        } 

9.两个对象值相同(x.equals(y)== true),但却可有不同的hashCode,这句话对不对?

不对,如果这两个对象x和y满足x.equals(y)== true,他们的哈希码(hashCode)应当相同.

java对于equals方法和hashcode方法是这样规定的:
(1)如果两个对象相同(equals方法返回true),那么他们的hashcode值一定要相同;
(2)如果两个对象的hashCode相同,他们不一定相同.当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set对象中,同时正价新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降).

关于equals和hashcode方法,很多java程序员都知道,很多人也就是仅仅知道而已,在Joshua Bloch的大作《Effective java》(很多软件公司,《Effective java》、《java 编程思想》以及《重构:改善既有代码质量》是java程序员必看书籍,如果你还没看过,那就赶紧去买一本吧)中式这样介绍equals方法的.

首先equals方法必须满足:
1)自反性(x.equals(x)必须返回true)
2)对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)
3)传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)
4)一致性(当x和y引用的对象信息没有被修饰时,多次调用x.equals(y)应该得到同样的返回值)
而且对于任何非null值的引用x,x.equals(null)必须返回false。

实现高质量的equals方法的诀窍包括:
1)使用==操作符检查“参数是否为这个对象的引用”
2)使用instanceof操作符检查“参数类型是否为正确的类型”
3) 对于类中的关键属性,检查参数传入对象的属性是否与之匹配;
4)编写完equals方法后,问自己它是否满足对称性,传递性,一致性;
5)重写equals时总是要重写hashcode
6)不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解

10是否可以继承String类
String 类是final类,不可以被继承.

继承String类本身就是一个错误的行为,对String类型最好的重用方式是关联(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)

11.当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

是值传递。java语言的方法调用只支持参数的值传递.当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用时改变,但对对象引用的改变是不会影响到调用者的.C++和C#中可以通过传引用或传输出参数的值。说明:java中没有传引用实在是非常不方便,在这一点java8中仍然没有得到改进,正是如此在java编写的代码中才会穿线大量的Wrapper类(将需要通过方法调用修改的引用置于一个Wrapper类中,在将Wrapper对象传入方法),这样的做法只会让代码变得臃肿,尤其是让从c和c++转型为java程序员的开发者无法容忍.

12.short s1 = 1;s1 = s1 + 1;有什么错?short s1 = 1; s1 += 1; 有什么错?

short s1 = 1; s1 = s1 + 1;(s1 + 1 运算结果时int型,需要强制转换类型)。
short s1 = 1; s1 += 1;可以正常编译.

13.Collection和Collections 的区别?

Collection是集合类的上级接口,继承他的接口主要有 Set 和 List;

Collections是针对集合类的一个辅助类,它提供一系列静态方法实现各种集合的搜索,排序,线程安全话等操作.

14.什么时候用assert?

assert:(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制.在实现中,assertion就是在程序中的一条语句,他对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明该程序已经处于不正常的状态下,系统将给出警告或退出.一般来说,assertion用于保证程序最基本,关键的正确性.assertion通常在开发和测试时开启,为了提高性能,在软件开发后,assertion检查通常是关闭的.

15.String s = new String(“XYZ”);创建的几个对象

这个问题是个坑,得这么回答.

一个对象或两个对象.
一个对象的回答:
new String(“XYZ”); 先去String池中去找,如果存在直接引用地址,在在常量池中创建s 这个对象 引用XYZ的地址
两个对象:
new String(“XYZ”);先去String池中去找,如果不存在则创建一个“XYZ”,之后再创建S对象 指向“XYZ”

16.Math.round(11.5)等于多少?Math.round(-11.5)等于多少?

(四舍五入)
Math.round(11.5)= 12 ;
Math.round(-11.5)= - 11;

17.数组有没有length()这个方法?String有没有length()这个方法?

数组没有length()这个方法,有length的属性
String有length()这个方法

18.重载和重写的区别?重载的方法能否根据返回类型区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同,参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常.重载对返回类型没有特殊要求.

方法重载的规则:
1.方法名一致,参数表中的顺序,类型,个数不同.
2.重载与方法的返回值无关,存在于父类和子类,同类中.
3.可以抛出不同的异常,可以有不同的修饰符

方法重写的规则:
1.参数类表必须完全与被重写方法的一致,返回类型必须完全与重写方法返回类型一致.
2.构造方法不能被重写,声明final的方法不能被重写,声明为static的方法不能被重写,但是能够再次声明.
3.访问权限不能比父类中被重写的方法的访问权限更低.
4.重写的方法能够抛出任何非强制异常(UncheckedException,也叫运行时异常),无论被重写的方法是否抛出异常.但是,重写的方法不能抛出新的强制性异常,或者比重写方法声明的更广泛的强制性异常,反之可以.

19.java语言的特性。

1.简单性
2.面向对象
3.可移植性—一次编译,到处运行
4.健壮性(自动回收机制,GC)
5.多线程

20.你所知道的集合类有哪些?有哪些方法?
Collection:
AbstractCollection, AbstractList, AbstractQueue, AbstractSequentialList, AbstractSet, ArrayBlockingQueue, ArrayDeque, ArrayList, AttributeList, BeanContextServicesSupport, BeanContextSupport, ConcurrentLinkedQueue, ConcurrentSkipListSet, CopyOnWriteArrayList, CopyOnWriteArraySet, DelayQueue, EnumSet, HashSet, JobStateReasons, LinkedBlockingDeque, LinkedBlockingQueue, LinkedHashSet, LinkedList, PriorityBlockingQueue, PriorityQueue, RoleList, RoleUnresolvedList, Stack, SynchronousQueue, TreeSet, Vector
Map<K,V>:
AbstractMap, Attributes, AuthProvider, ConcurrentHashMap, ConcurrentSkipListMap, EnumMap, HashMap, Hashtable, IdentityHashMap, LinkedHashMap, PrinterStateReasons, Properties, Provider, RenderingHints, SimpleBindings, TabularDataSupport, TreeMap, UIDefaults, WeakHashMap
常用方法:
list:
boolean add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void clear()
移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean equals(Object o)
比较此 collection 与指定对象是否相等。
int hashCode()
返回此 collection 的哈希码值。
boolean isEmpty()
如果此 collection 不包含元素,则返回 true。
Iterator iterator()
返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int size()
返回此 collection 中的元素数。
Object[] toArray()
返回包含此 collection 中所有元素的数组。
T[]
toArray(T[] a)
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
map:
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int hashCode()
返回此映射的哈希码值。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
Set keySet()
返回此映射中包含的键的 Set 视图。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()
返回此映射中的键-值映射关系数。
Collection values()
返回此映射中包含的值的 Collection 视图。

21用最有效的方式计算2乘以8等于几
System.out.println(2 << 3);

22.String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?
简单的来说:String类中使用final关键字字符数组保存字符串,private final char value[],
所以String对象是不可变的.而StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[] value 但是没有final关键字修饰,所以这两种对象都是可变的.
StringBuilder 与 StringBufffer 的构造方法都是调用父类构造方法也是AbstractStringBuilder实现的
线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全.AbstractStringBuilder是StringBuilder与StringBuffer的公共类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法.StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的.StringBuilder并没有对方法进行加同步锁,所以是非线程安全的
性能
每次对String类进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象.
StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用.相同情况下使用Stringbuilder相比使用StringBuffer仅能获取10%-15%左右的性能提升,但却冒着线程不安全的风险.
对于三者使用的总结:

  1. 操作少量的数据 = String
  2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffffe
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值