@面向对象的特征/Java的特性
- 封装、继承、多态、抽象
- 封装:把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口
- 继承:从现有类中得到继承信息来创建新类的过程,提供继承信息的类被称为父类,得到继承信息的类被称为子类。
- 多态:允许不同子类型的对象对同一消息作出不同的响应,可分为编译时的多态性和运行时的多态性。
- 抽象:将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么
1、String、StringBulider、StringBuffer的区别
- String是不可变的,尝试去修改只是会新生成一个字符串对象,而StringBulider、StringBuffer是可变的
- StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有。StringBulider线程不安全,但快,StringBuffer线程安全,但是慢
2、JDK 和 JRE 有什么区别?
- jdk:java开发工具
- jre:java运行环境
- jdk包括jre,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib就是JVM工作所需要的类库。
3、== 和 equals 的区别是什么?
- 对于基本数据类型,int、float等,==比较的是值
- 对于引用类型,==比较的是内存地址
- equals不能用于基本类型的比较;
- 如果没有重写equals,equals就相当于==;
- 如果重写了equals方法,equals比较的是对象的内容;
4、ArrayList与LinkedList的区别
- 底层实现不同,ArrayList是数组实现,LinkedList是链表实现
- ArrayList适合随机查找(数组下标),因为LinkedList要移动指针
- LinkedList适合新增与删除(链表指针指向),因为ArrayList要移动数据
(附)你知道哪些线程安全的集合?
- 得分点:Collections、java.util.concurrent (JUC)
- java.uti包中的集合类大部分都是非线程安全的,例如:ArrayList,LinkedList,HashMap等等,但也有少部分是线程安全的,像是Vector和Hashtable,它们属于很古老的API了,是基于Synchronized实现的,性能很差,在实际的开发中不常用
- 一般可以使用Collections工具类中的syncheronizedXxx()方法将非线程安全的集合包装成线程安全的类。在java5之后可以使用concurrent包提供的大量的支持并发访问的集合类,例如ConcurrentHashMap/CopyOnWriteArrayList等
- 区别:JDK8中,ConcurrentHashMap的底层数据结构与HashMap一样,也是采用“数组+链表+红黑树”的形式。同时,它又采用锁定头节点的方式降低了锁粒度,以较低的性能代价实现了线程安全
5、String 类的常用方法都有那些?
(1)String类的获取功能
- length():获取字符串长度;
- charAt(int index):获取指定索引位置的字符;
- indexOf(int char):返回指定字符在此字符串中第一次出现处的索引;
- substring(int start):从指定位置开始截取字符串,默认到末尾;
- substring(int start,int end):从指定位置开始到指定位置结束截取字符串;
(2)常见String类的判断功能
- equals(Object obj): 比较字符串的内容是否相同,区分大小写;
- contains(String str): 判断字符串中是否包含传递进来的字符串;
- isEmpty(): 判断字符串的内容是否为空串;
- startsWith(String str): 判断字符串是否以传递进来的字符串开头;
(3)常见String类的转换功能
- String.valueOf(): valueOf可以将任意类型转为字符串;
- toLowerCase(): 把字符串转成小写;
- 创建格式化对象 format(),多用于日期
- concat(String str): 把字符串拼接;
String str=new String("abcdefg"); String str1=new String("hijklmn"); String concat = str.concat(str1);
6、final、finally、finalize 有什么区别?
- final用于修饰类,变量,方法,,修饰的类不能被继承、修饰的变量不能被修改,修饰的方法不能被重写。
- finally用于抛异常,无论异常是否发生,都会执行finally代码块的语句,常用于一些流的关闭,比如JDBC连接时关闭connect等。
- finalize方法用于垃圾回收。
7、常见的异常类有哪些?
- NullPointerException:空指针异常
- SQLException:数据库异常
- IndexOutOfBoundsException:数组下标越界异常
- FileNotFoundException:找不到文件异常
- NoSuchMethodException:找不到方法异常
- ArrayStoreException:将错误类型的对象存储到一个对象数组时抛出的异常
- NumberFormatException:将字符串转换成数字时失败的异常
- IllegalArgumentException :向方法传了一个不合法或不正确的参数的异常
- ArithmeticException:异常的运算条件的异常,比如除零错误
8、hashcode是什么?有什么作用?
- hashcode是通过hash函数算来的一个十进制的整数,一个对象有唯一确定的哈希值
- 如果两个对象equals相等,那么这两个对象的HashCode一定也相同
- 如果两个对象的HashCode相同,不代表两个对象相同,只能说明这两个对象在散列存储结构中,存放于同一个位置
- 我们在重写equals方法后,也尽量重写hashcode方法,通过一定的算法,使他们在equals相等时,也会有相同的hashcode值。
9、HashMap 和 Hashtable 有什么区别?
- HashMap是线程不安全的,HashTable是线程安全的
- HashMap中允许键和值为null,HashTable不允许
- HashMap的默认容器是16,为2倍扩容,HashTable默认是11,为2倍+1扩容
10、Synchronized锁
- 多个线程操作共享数据时,加锁保证访问共享数据的线程安全性。
11、Java反射?
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
12、为什么要使用克隆?如何实现对象克隆?深拷贝和浅拷贝区别是什么?
(1)什么时候使用克隆?
想对一个对象进行复制,又想保留原有的对象进行接下来的操作,这个时候就需要克隆了。
(2)如何实现对象克隆?
实现Cloneable接口,重写clone方法;
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
BeanUtils,apache和Spring都提供了bean工具,只是这都是浅克隆。
(3)深拷贝和浅拷贝区别是什么?
浅拷贝:仅仅克隆基本类型变量,不克隆引用类型变量;
深克隆:既克隆基本类型变量,又克隆引用类型变量;
13、常见的集合
- Collection接口的子接口有Set接口与List接口
- Map接口实现的集合:HashMap、TreeMap、HashTable
- Set接口实现的集合:HashSet、TreeSet、LinkedHashSet
- List接口实现的集合:ArrayList、LinkedList、Stack、Vector
- 线程安全的集合类:HashTable,Vector,ConcurrentHashMap
14、重写(Override)和重载(Overload)
- 重写与重载是java多态性的不同表现
- 重写:在子类中把父类本身有的方法重新写一遍
- 重载:在一个类中定义了多个同名的方法但是返回的参数不同
15、抽象类和接口的区别
- 可以实现多个接口但是只能继承一个类
(1)接口
- 接口使用interface修饰
- 接口是公开的,不能有私有的方法或变量,接口中的所有方法都没有方法体
- 一个类通过(implements实现)继承接口的方式,从而来继承接口的抽象方法
- 接口并不是类,虽然编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法,除非实现接口的类是抽象类,否则该类要定义接口中的所有方法
(2)抽象类
- 抽象类用abstract修饰
- 抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体
- 抽象方法必须在抽象类中
- 抽象类只能单继承
接口可以被看作是抽象类的变体,接口中所有的方法都是抽象的,可以通过接口来间接的实现多重继承。接口中的成员变量都是static final类型,由于抽象类可以包含部分方法的实现,所以,在一些场合下抽象类比接口更有优势
16、HashMap1.7和1.8的区别
- JDK1.7采用的是数组+链表的形式,而JDK1.8在数组容量大于64且链表长度大于8的情况下会使用红黑树
- jdk1.7中当哈希表为空时,会先调用inflateTable()初始化一个数组;而1.8则是直接调用resize()扩容
- 插入键值对的put方法的区别,1.8中会将节点插入到链表尾部,而1.7中是采用头插
- jdk1.7中的hash函数对哈希值的计算直接使用key的hashCode值,而1.8中则是采用key的hashCode异或上key的hashCode进行无符号右移16位的结果,避免了只靠低位数据来计算哈希时导致的冲突,计算结果由高低位结合决定,使元素分布更均匀
- 扩容时1.8会保持原链表的顺序,而1.7会颠倒链表的顺序;而且1.8是在元素插入后检测是否需要扩容,1.7则是在元素插入前
- HashMap在JDK1.8之后不再有死循环的问题,JDK1.8之前存在死循环的根本原因是在扩容后同一索引位置的节点顺序会反掉
- jdk1.8是扩容时通过hash&cap==0将链表分散,无需改变hash值,而1.7是通过更新hashSeed来修改hash值达到分散的目的
- HashMap是非线程安全的,在并发场景下使用ConcurrentHashMap来代替
17、Java访问修饰符
-
Java提供了4种访问控制符,主要用于控制其他类是否可以访问某一类中的属性或方法,从而实现数据封装。四种访问控制符的权限由大到小为public(公共)、protected(保护)、default(默认)、 private(私有)
-
public(公共):具有公共访问权限。其修饰的属性或方法可以被任何类调用
-
protected(保护):具有子类访问权限。其修饰的属性或方法可以被同一包下的类使用,也可以被其它包下的子类使用,但不能被其它包下的其他类使用
-
default(默认):具有包访问权限,可以被同一包下的类使用。
-
private(私有):其修饰的属性或方法只能被自己类中的方法使用
Java中private只能被本类访问。但是还可以利用java中的反射从外界调用private变量或方法
18、Java中值传递和引用传递
- 基本类型的变量保存原始值,所以变量就是数据本身
- 引用类型的变量保存引用值,所谓的引用值就是对象所在内存空间的“首地址值”,通过对这个引用值来操作对象
- 值传递:
在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到函数中,这样如果在函数中对该值(形参的值)进行了操作将不会影响实参的值。因为是直接复制,所以这种方式在传递大量数据时,运行效率会特别低下 - 引用传递:
引用传递弥补了值传递的不足,如果传递的数据量很大,直接复过去的话,会占用大量的内存空间,而引用传递就是将对象的地址值传递过去,函数接收的是原始值的首地址值。在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象
19、JDK1.7和JDK1.8的区别
- HashMap的区别:JDK1.7采用的是数组+链表的形式,而JDK1.8在数组容量大于64且链表长度大于8的情况下会使用红黑树;jdk1.8 以前,链表时头插入,之后为尾插入
- 可以Catch多个异常
- 泛型实例创建可以通过类型推断简化,new对象后边的泛型可以不用写,直接< >
- Annotation注解:支持多重注解
20、反射和泛型
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
- 泛型,即“参数化类型”,通过泛型指定的不同类型来控制形参具体限制的类型,也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法比如
//泛型就是一种未知的类,将未知的类型声明在集合、对象上,泛型的默认类型为Object
List<String> list =new ArrayList<String>();
//这是将String类型指定在list这个集合内,这个集合存储或者读取的类型只能为String类型
反射的解释与应用
- 比如说,IDEA这个软件是先开发好的,我们的的程序是后写的。为什么IDEA能给出关键字提示,当你输入一个对象,会有一个列表列出所有的对象的方法,这个就是靠的反射。
- 比如说,我们可以通过一个对象获得完整的包名和类名,用getClass().getName()
- java中private只能被本类访问。但是还可以利用java中的反射从外界调用private变量或方法
泛型的解释和应用
- 作用在类上时,我们可以使用泛型做结果集比如
public class Result<T>
- 作用在方法上时:
public static <T> Result<T> success(T data){
Result<T> result=new Result<>();
result.setCode("0");
result.setMassage("成功!");
result.setData(data);
return result;
}
- 作用在对象上时:
List<String> list =new ArrayList<String>();
- 使用泛型后对象或者集合内只能放入指定的数据类型,避免出现对象或者集合内的对象在多态使用的时候出现类型转换异常(java.lang.ClassCastException),可以保证对象或者集合的安全性
- 指定了类型后,对象、集合或方法内只能使用对应的类型,可以减少类型的转换操作(在没有指定类型是类型转换必须使用 instanceof 关键字来进行判定),缩减了代码了,方便了程序开发
21、内部类
1、成员内部类(普通内部类)在一个类里面直接定义一个类
public class OuterClass {
public class InnerClass {
public void test() {
}
}
}
- Inner类定义在Outer类的内部,相当于Outer类的成员变量的位置,Inner类可以使用任意访问修饰符,如:public、private、protected等
- Inner类中定义的test()方法可以访问Outer类中的数据,不受访问控制符的影响
- 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象
- 如果外部类和内部类具有相同的成员变量或方法,内部类可以直接访问内部类的成员变量或方法,但如果内部类访问外部类的成员变量或者方法时,需要使用this关键字
2、静态内部类
- 静态内部类就是用static修饰的内部类
public class OuterClass {
String name="张三";
public static class InnerClass() {
public void show() {
System.out.println("name= "+ new OuterClass().name )
System.out.println("name= "+ OuterClass().name )
}
}
}
- 静态内部类不能直接访问外部类的非静态成员,但,可以通过new 外部类().成员的方式访问
- 如果外部类的静态成员与内部类的静态成员相同, 可以通过"类名.静态成员"来访问外部类的静态成员;如果不同,可以直接调用外部类的静态成员名
- 创建静态内部类的对象时,不需要外部类的对象,可以直接创建
3、方法内部类
public class OuterClass {
String name="张三";
public void show() {
class InnerClass() {
int a=3;
public void print() {
System.out.println("a= "+ a )
System.out.println("name= "+ name )
}
}
}
}
- 方法内部类就是定义在外部类的方法中,方法内部类只在该方法内可以用
- 由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符
装箱与拆箱
- Java为每种基本数据类型都提供了对应的包装器类型,如int–>Integer
- 在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);
而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了:
Integer i = 10;
这个过程中会自动根据数值创建对应的 Integer对象,这就是装箱。
那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型:
Integer i = 10; //装箱
int n = i; //拆箱
- 注意:在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象