Java基础整理 Q&A

AOEBIU 2020/1/8 Java

java基础

==和equals的区别?
==在基本类型之间是比较值是否相同,引用类型之间是比较引用是否相同
equals 本质就是==,在String,Integer等引用类型重写了euqal使它变成值比较
两个对象的 hashCode() 相同, 那么 equals() 也一定为 true吗?
和查字典一样,会从先从哈希值(目录)中查询,然后在哈希值指向的链表中逐一查询(equals),即使哈希值相同(页码相同),里面链表的元素也各个不同(一个页码里的单词也各个不同)
这也HashMap的查询原理,是一种用空间换时间的思维方式
final 在 Java 中有什么作用?
final可以修饰的三处地方
类上:这个类不能被继承(如String),方法上:这个方法不能被重写,变量上:这个变量变为常量
final修饰符可以起到保护作用
Math. round(-1. 5) 等于多少?
Math.round是四舍五入,小数为5所以向上(大)取整,结果为-1
除此之外还有Mah.floor:直接向上取整, Math.ceil:直接向下取整
String 属于基础的数据类型吗?
首字母是大写所以不是
java的基本类型只有8中byte,char,short,int,long,float,double,boolean
以及对应的引用类型Byte,Character,Short,Long,Float,Double,Boolean
有些地方把void也算,所以可能有9中
Java 中操作字符串都有哪些类?它们之间有什么区别?
有String,StringBuffer,StringBuilder
String每次操作都会生成一个新的String对象性能较低,所以诞生了StringBuffer和StringBuilder
String,StringBuffer其实内部就是一个字符数组,不过后者留有冗余长度,如果长度超过了就会再分配一个新的拥有冗余长度的数组,把原来的数据复制到新的数组中
StringBuffer 和 StringBuilder 一样不过前者才是线程安全的,后者性能更高
String str="x"与 String str=new String("x")一样吗?
直接赋值会分配到常量池中而通过new则分配到堆内存中
并且当再创一个String str2="x",java虚拟机会先从常量池中先检索是否已有“x”
而String str3=new String("x"),通过构造方法创建的只会分配到堆内存中
对象与引用的区别?
User user=new User();
首先User user声明对象的引用并分配在栈内存中,然后new User()调用User类的构造方法,并将User分配堆内存中,最后User user=new User()将对象的引用指向对象
一个引用可以指向一个对象,也可以不指向,也可以指向多个
什么是多态?
一般分为编译时多态:就是方法的重载,通过方法不同的签名(方法的参数,不包括返回值)来区分不同的方法
大部分情况多态指的都是运行时多态:也叫作动态绑定,只有运行时才能知道调用的是哪个子类的方法
提高了代码的扩展性,不过前提需要父类引用指向子类对象
多态的三种情况:父类的方法子类没有重写,编译通过调用父类的方法
父类的方法子类重写,编译通过调用子类的方法
子类的特有方法,编译失败,这种情况下不能使用多态
如何将字符串反转?
最简单的方法是通过StringBuffer或StringBuilder的reverse方法
也可以将字符串转换成字符数组,遍历这个字符串数组每遍历一次便拼接在一个新的字符串上
String类常用方法?
indexOf() 返回指定字符的第一个索引
trim() 去除字符串两端空白
charAt() 返回指定索引处的字符
split() 通过指定分割符分割字符串,返回一个分割后的字符串数组
replace() 将一个字符串中的部分替换成另一个字符串
length() 返回字符串长度
toLowerCase() 将字符串转成小写字母
toUpperCase() 将字符串转成大写字符
substring() 通过下标截取字符串(如果非数字会通过ASCII转换成数字)
equals() 字符串比较
File类的常用方法都有哪些?
exists() 检测文件的路径是否存在
createFile() 创建文件
createDirectory() 创建文件夹
delete() 删除一个文件或目录
mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录
mkdir() 创建此抽象路径名指定的目录
length() 返回文件的字节大小
抽象类和普通类的区别?
两者只有抽象类才可以有抽象方法
抽象类没有方法的实现,也不应该可以实例化
不能被final修饰,因为抽象类一定要被继承
接口和抽象类有什么区别?
抽象类才能有构造方法(但是构造方法不能被继承需要super关键字)
类只能单继承,接口可以多继承
接口里的修饰符默认有public abstract修饰,成员变量默认有public static final修饰
java8以后允许接口有实现方法,但是需要default覆盖掉public或者增加static修饰符

java数据结构

Collection和Collections有什么区别?
Collection是所有集合的接口
Collections是一个工具类提供对集合的一些简便操作,完全不是一码事
Collections里的都是静态方法
reverse 反转
shuffle 混淆
sort 排序
swap 将两个下表的元素交换位置
rotate 向右滚动
synchronizedList 将这个集合变成线程安全的
unmodifiableCollection 集合只读
Set与List之间的区别?
两者都是接口
List和Set都是有序的集合,能通过索引获取对应的元素,有序的插入
List可以存储不同的元素,Set不可以
ArrayList和LinkedList之间的选择?
ArrayList是数组结构,所以定位很快。相当于一个可以扩展的数组。删除数据慢,每增删一个元素这个元素之后的所有元素都会受到影响
LinkedList 是链表结构,所以定位慢。每个元素指向下一个元素的地址。删除数据快,每增删一个元素除了这个元素之外的两个元素其他元素都不会受到影响
HashMap和TreeMap之间的选择?
对于在 Map 中插入、删除、定位一个元素,HashMap 是最好的选择,但如果要对一个 key 集合进行有序的遍历,那 TreeMap 是最好的选择。
HashSet和HashMap的关系?
HashSet内部封装了一个HashMap,为HashSet添加元素实际上就是为HashMap添加元素,value为固定值,所有方法都是直接调用Map的,迭代就是通过Map的keySet方法获取键
Dueue和Queue接口之间的区别?
LinkedList同时实现了这两个接口
双向链表Deque代表双向链表结构
双向链表常用方法
addLast 在尾部插入
addFirst 在头部插入
getFirst 获取头部的元素 获取不会导致元素丢失
getLast 获取尾部的元素
removeFirst 取出头部的元素 取出会导致元素丢失
removeLast 取出尾部的元素
接口队列Queue代表先进先出的队列
接口队列的常用方法
offer 在最后添加元素
poll 取出第一个元素
peek 查看第一个元素
如何理解二叉树?
TreeMap底层实现了红黑树,红黑树就是一种算法更复杂的二叉树,可以大大增加运行效率
将数据插入二叉树,小于等于的放左边,大于等于的放右边
//简单的二叉树
public void add(Object v) {
if (null == value) {
value = v;
}
else {
if ((Integer) v - ((Integer) value) <= 0) {
if (null == leftNode){
leftNode = new Node();
}
leftNode.add(v);
}
else {
if (null == rightNode){
rightNode = new Node();
}
rightNode.add(v);
}
}
}
迭代器 Iterator 是什么?
所有集合都可以使用迭代器,迭代器取代了Enumeration,并且支持迭代过程中移除元素
List<Integer> list = new ArrayList<>();
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer next = it.next();
if (next <= 5) {
list.remove(next);
}
}
System.out.println(list);
值得一提的是,增强for循环底层就是迭代器
除此之外还有ListIterator,它只能遍历List集合,并且能够双向遍历

java线程交互

线程和进程的区别?
进程是系统层面的,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程是在进程内部同时做的事情,是操作系统能够进行运算调度的最小单位
守护线程的概念是?
当一个进程里,所有的线程都是守护线程的时候,结束当前进程
Thread的setDaemon(true) 将这个线程改为守护线程
守护线程通常会被用来做日志等工作,即使是死循环或死锁也能退出
创建线程有哪几种方式?
继承Thread重写run方法
实现Runnable接口重写run方法
实现Callable接口重写call方法
前两种方法之间选择一般推荐第二种因为接口更灵活
Callable 可以拿到返回值 相当于Runnable的补充,不过用到的场景不多
什么是线程不安全,如何解决?
就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
解决方法
1.使用synchronized修饰符,如果修饰在方法上同步对象就是this
//这两个方法效果一致
public synchronized void test() {
System.out.println("只有我能进来,哈哈");
}
public void test2() {
synchronized (this) {
System.out.println("只有我能进来,哈哈");
}
}
2.直接使用线程安全的类如StringBuffer,它的所有方法都是由synchronized修饰的
3.使用Lock,可以达到和synchronized一样的效果
Lock lock = new ReentrantLock();
Thread t=new Thread(){
public void run(){
lock.lock();
System.out.println("只有我能进来,哈哈");
lock.unlock();
}
};
t.start();
两者的区别,synchronized是java的关键字而Lock是代码层面实现的
什么是死锁,如何防止死锁?
线程1占用锁a并试图去获取锁b,线程2占用锁b并试图去获取锁a
线程1等待线程2解锁锁a,线程2等待线程1解锁锁b,然后就会永远等下去
使用Lock的tryLock方法避免死锁,或者谨慎使用synchronized
//使用lock的tryLock方法防治死锁,超时将不再获取,返回布尔值
flag= lock.tryLock(1,TimeUnit.SECONDS);
线程中常用的方法?
Thread sleep 线程休眠一段时间与Object的wait方法相类似,不过sleep不会释放锁,而wait会
Object wait() 只能在synchronized修饰的方法或代码块中调用
Object notify() 唤醒一个线程 这两个方法可以唤醒wait中的线程
Object notifyAll() 唤醒所有线程
Thread setPriority() 设置线程的优先级
Thread setPriority() 临时暂停
Thread join() 加入线程
什么是原子访问?
不可以中断的操作如int i;
使用AtomicInteger进行原子访问也能达到线程安全
AtomicInteger atomicI =new AtomicInteger(); //atomicI 初始值为0
atomicI.decrementAndGet(); //自减
atomicI.incrementAndGet(); //自增
atomicI.addAndGet(6); //加6

反射

什么是反射?如何使用反射?
反射是指程序在运行状态中,对于任意一个类,都可以知道这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意方法和属性。反射的特点是不通过new关键字实例化对象,减少程序的耦合度,所有JAVA框架都用到了反射
forName() 通过Class类的静态方法
getClass() 通过实例化对象获取Class对象
x.class 通过类的静态成员对象(每个类都有隐含的静态成员class)
其它使用反射中常用的方法?
Class newInstance() 直接通过无参构造方法实例化
Constructor newInstance() 通过指定构造方法实例化
Class getFields() 获取所有公共成员变量
Class getField() 获取指定的公共成员变量
Class getDeclaredField() 获取指定成员变量
Class getConstructors() 获取所有公共的构造方法
Class getDeclaredConstructors() 获取所有构造方法
Class getConstructor() 获取指定构造方法
Method getMethods() 获取所有公共方法(包括父类)
Method getDeclaredMethods 获取所有方法(不包括父类)
Method setAccessible(true) 取消访问检查
Method invoke() 反射调用
//简单的反射使用
try {
Class aClass = Class.forName("xx.xx.user");
Object con = aClass.newInstance();
Method method = aClass.getMethod("getName");
method.invoke(con);
} catch (Exception e) {
e.printStackTrace();
}
原本是对象调用方法,现在是方法调用对象

java设计模式

什么是序列化?
序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。
要序列化的类需要继承Serializable并提供一个private static final long serialVersionUID的成员变量,Serializable接口内部没有任何代码,只是一个标记
//对象流的使用
User user = new User();
File f = new File("d:/");
try {
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
oos.writeObject(user); //输入
User h2 = (User) ois.readObject(); //输出
} catch (Exception e) {
e.printStackTrace();
}
ajax请求的实体类一般也需要实现序列化接口.如果父类继承了序列化接口,子类也可以被序列化
什么是代理模式?
代理模式是不改变源码的情况下,对已有功能的增强
静态代理 通过new的方式对已有方法增强
public class BigUser implements people {
private User user = new User();
public void getName() {
System.out.println("出生了");
user.getName();
System.out.println("死掉了");
}
public static void main(String[] args) {
BigUser user=new BigUser();
user.getName();
}
}
动态代理 通过继承接口java官方的InvocationHandler接口或者基于子类第三方jar包org.sonatype.sisu.inject
Proxy.newProxyInstance(ClassLoder,class[],InvocationHandler) 参数解释
ClassLoder:用于加载代理对象字节码
Class:让代理对象和被代理对象有相同方法
InvocationHandler:增强代码需要实现invoke方法
Object invoke(proxy,method,args[]) 反射调用
proxy:代理对象的引用
method:执行的方法
args:需要的参数
return:和被代理对象相同类型的返回值
Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)参数解释有部分重复的参考上面
Class:指定被代理对象的字节码
Callback:指定增强的代码,一般写为这个接口的子接口new MethodInterceptor需要实现里面的intercept方法
methodProxy:当前执行方法的代理对象(一般用不到)
两个使用方式几乎相同,所以只详细说明一种
final Producer producer = new Producer();//匿名内部类的参数需要由final修饰
Producer pr = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
if ("saleProduct".equals(method.getName())) { //需要要增强的方法
Float money = (Float) args[0]; //获得这个方法的第几个参数
returnValue = method.invoke(producer, money * 0.8f);//调用这个方法
return returnValue;// *如果返回值为void可以不写return(因为底下抛了异常所以要写)
}
if ("afterService".equals(method.getName())) {
Float money = (Float) args[0];
returnValue = method.invoke(producer, money * 0.8f);
return returnValue;
}
if ("toString".equals(method.getName())) {
returnValue = method.invoke(producer);
return returnValue + "啦啦啦";
}
throw new Throwable();
}
});
pr.saleProduct(30000f); //调用里面的三个方法
pr.afterService(400f);
System.out.println(pr);
finally、finalize 有什么区别?
finally是try-catch-finally的最后一部分,只要走到try的代码块中无论如何都会进入finally代码块中,return的结果也会覆盖
finalize是当该类被JAVA垃圾回收机制回收的时候会调用这个方法
举例几个设计模式?
1.单例模式,保证类只被实例化一次,单例模式分为懒汉式和饿汉式
/**
* 饿汉式,私有构造方法不让外部new,并且覆盖默认公有构造方法,
* 提供一个共有的静态方法,让外部获取实例化对象
* 但是又因为成员变量是静态的所以不能在内存中开辟多个该实例对象
*/
public class model {
private static model mo = new model();
private model() {}
public static model getOnlyInstance(){
return mo;
}
}
//懒汉式,最大的区别就是成员变量默认是null,并且方法执行之前还会判断是否有指向,所以性能比饿汉式低
public class model {
private static model mo;
private model() {}
public static model getOnlyInstance() {
if (null == mo) {
mo = new model();
}
return mo;
}
}
2.代理模式:不修改源码的基础上对已有的方法增强(上面已经有了)
3.工厂模式:解耦,降低类与类之间的关系
//接口
public interface People{
void eat();
}
//接口的实现类1
public class child implements People{
public void eat() {
System.out.println("小孩子会吃饭");
}
}
//接口的实现类2
public class man implements People{
public void eat() {
System.out.println("成年人会吃饭");
}
}
/**
* 工厂类,通过传入不同参数来创建不同对象
*/
public class humanFactory {
//使用getHuman方法来获取对象
public People getHuman(String type) {
Type = Type.trim();
if ("child".equalsIgnoreCase(type)) { //不区分大小写比较
return new child();
} else if ("man".equalsIgnoreCase(type)) {
return new man();
}else{
throw new RuntimeException("请传入一个合法参数");
}
}
}
觉得这篇文章对你有有帮助吗?
发表评论
0
说扒~

条回复 个点赞
为什么没人来抢占个沙发呢?