Java基础
Q:面向对象三大特性
- 封装、继承、多态;
- 从一定角度上看,封装和继承是为了多态而准备的
多态
存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象
实现的三种方式
- 重写
- 接口
- 抽象和抽象方法
Q:类的初始化顺序依次是?
(静态变量、静态代码块)>(变量、代码块)>构造方法
Q:Integer的比较
当直接给Integer赋int值时,如果值得范围为[-128,127],Integer直接从缓存中取Integer对象,因此,当直接赋的int值处于[-128,127]时,Integer对象为相同的对象。而通过new方式取的Integer对象,是直接从堆里面分配的对象,因此,不管具体的int值为多少,==判断的结果都是false
Q:equals与==的区别
- ==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
- ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
- ==指引用是否相同, equals()指的是值是否相同
Q:Java几种集合
- List: List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set: 不允许重复的集合。不会有多个元素引用相同的对象。
- Map: 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
Q:对java的集合List有了解吗?
- 对于随机访问 get、set ArrayList效率优于LinkedList,因为LinkedList要移动指针遍历;
- 对于新增add和删除remove操作,Linkedlist要优于ArrayList,因为ArrayList要移动数据
- Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
nul | ArrayList | LinkedList | Vector |
---|---|---|---|
底层 | 动态数组 | 链表 | 动态数组 |
特性 | 随机访问get和set性能高(Linked需要移动指针遍历) | 新增删除操作add和remove性能高(因为ArrayList要移动数据)(ArrayList在添加和删除的时候,底层是创建一个新的数组,而LinkedList却只要修改一下指针就ok了) | 支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢 |
使用 | 查询多,使用ArrayList | 添加\删除多,使用LinkedList | 和ArrayList一样 |
Q:对java的集合Map有了解吗?
Map | HashMap | HashTable | ConcurrentHashMap |
---|---|---|---|
底层 | 数组+链表,数据量大时会转化为红黑树进行保存 | 和hashmap差不多,但是key、value不允许为null,线程安全,效率较慢 | 线程安全,ConcurrentHashMap采用了分段锁技术,没有同Hashtable一样锁住全部数据,效率较比HashTable高 |
Q:调用start()方法和调用run()方法的区别
1、start()方法是开启了一个线程,会自动调用run()方法,此时程序会自动往下执行,此时不等start()方法执行完。
2、如果线程直接调用run()方法,则程序会等run()方法执行完再往下执行。
Q:对反射有了解吗?
基础
- 主要涉及到的类:Class类、Field类(类属性)、Method类(类方法)、Constructor类(构造方法)
- 带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法。
- 访问私有方法、成员函数
使用场景
- 组件化时,通过反射获取各个组件初始化类,进行初始化(实现了统一接口)
- 使用ViewPager + Fragment 时,如果传入一个包含Fragment的List,会造成对Fragment的强引用,造成内存泄漏
简单使用
获取类、实例化
Class<?> mClass = Class.forName("reflect.Person"); // 必须是全路径
System.out.println(mClass.getName()); // 获取类名
// output : reflect.Person
Person person = (Person) mClass.newInstance();
person.setAge(1);
person.setName("LiHan");
System.out.println(person.getName() + "age is " + person.getAge());
// output : LiHanage is 1
获取构造函数
Constructor[] constructors = mClass.getConstructors(); // 获取public构造方法包括私有构造方法
Constructor[] constructors2 = mClass.getDeclaredConstructors(); // 获取所有构造方法包括私有构造方法
// 使用私有构造函数
Constructor constructor = mClass.getDeclaredConstructor(String.class); // 获取到私有构造函数
constructor.setAccessible(true);
Person person2 = (Person) constructor.newInstance("person2");
System.out.println(person2.getName());
获取、修改成员变量
...
//接上
// 获取公共成员变量
Field[] fields = mClass.getFields();
// 获取所有成员变量
Field[] fields2 = mClass.getDeclaredFields();
// 修改成员变量
Person person1 = (Person) mClass.newInstance();
Field tag = mClass.getField("TAG");
Field name = mClass.getDeclaredField("name");
tag.setAccessible(true);
name.setAccessible(true);
tag.set(person1,"newTAG"); // 传入操作对象和值
name.set(person1,"newName");
System.out.println(person1.TAG + " " + person1.getName());
获取、使用方法
// 获取到所有方法(包括Object类的)
Method[] methods = mClass.getMethods();
// 获取到“私有方法” 即Person类的所有方法
Method[] declaredMethods = mClass.getDeclaredMethods();
Method setTAGMethod = mClass.getDeclaredMethod("setTAG",String.class);
setTAGMethod.setAccessible(true);
setTAGMethod.invoke(person1,"reflectName");
System.out.println(person1.TAG);
Q:String,StringBuffer与StringBuilder的区别
- String 不是基本数据类型,而是一个对象
- String 底层是一个final类型的 char[ ] 因此String的值是不可变的,每次对String的操作都会产生新的String对象;
- 创建字符串的时候先查找字符串缓冲池中有没有相同的对象,如果有相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串缓冲池中创建该对象,然后将该对象的应用返回。
- StringBuffer 和 StringBuilder 底层是可变的 char[ ]
String | StringBuilder | StringBuffer |
---|---|---|
不可变 | 可变 | 可变 |
无 | 线程安全,速度较慢 | 线程不安全,速度较快 |
Q:强软弱虚
- 强引用:在内存不足时不会被回收。平常用的最多的对象,如新创建的对象。
- 软引用:在内存不足时会被回收。用于实现内存敏感的高速缓存。
- 弱引用:只要GC回收器发现了它,就会将之回收。用于Map数据结构中,引用占用内存空间较大的对象。
- 虚引用:在任何时候都可能被垃圾回收器回收。
Q:Java 浅克隆和深克隆的区别
- 浅克隆:
是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
(子类对象还是指向同一个地址)
- 深克隆:
不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。(引用的对象也进行了克隆)
- 实现深克隆:
重写父类的clone方法,在clone调用包含的子类的clone克隆方法
Q:抽象类和接口的区别有哪些
- 抽象类要被子类继承,接口要被类实现。
- 接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
- 接口是设计的结果,抽象类是重构的结果。
- 抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。
- 抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
- 抽象类主要用来抽象类别,接口主要用来抽象功能。
多线程
Q:线程的几个常用方法
- start 开启一个线程
- run 执行任务的具体逻辑
- join 阻塞当前调用它的线程,等待join执行完毕,当前线程继续执行。
- Thread.currentThread() 获取执行当前方法的线程
- Thread.yield() 降低线程优先级,可能会使线程进入暂停状态
- Thread.sleep(ms) 使当前线程在指定时间内休眠
Q:wait和sleep方法的不同
- wait()来自Object类,sleep()来自Thread类
- 调用 sleep()方法,线程不会释放对象锁。而调用 wait() 方法线程会释放对象锁;
- sleep()睡眠后不出让系统资源,wait()让其他线程可以占用 CPU;
- sleep(millionseconds)需要指定一个睡眠时间,时间一到会自然唤醒。而wait()需要配合notify()或者notifyAll()使用
Q:多个线程如何按顺序执行
使用Thread的join方法
先执行 t1,ti执行完后再执行t2:
t1.start();
t1.join();
t2.start();
加入synchronized关键字