java基础——面试篇上

java基础知识

java基础。



前言

java基础。


一、运算符

1、运算符 &和&&、|和||的区别?

& 按位与操作

只有对应的两个二进制数为1时,结果位才为1
1&1 = 1
1&0 = 0
0&1 = 0
0&0 = 0

| 按位或操作

有一个为1的时候,结果位就为1
1|1 = 1
1|0 = 1
0|1 = 1
0|0 = 0

& 和 && 都可以实现 和 这个功能
区别:& 两边都运算,而 && 先算 && 左侧,若左侧为false 那么右侧就不运算,判断语句中推荐使用 &&,效率更高

| 和 || 和上面类似
区别:||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断

把&&和||称之为短路运算符

举例:用最有效率的方法计算2乘以8

原理:将一个数左移n位,相当于乘以2的n次方,位运算是CPU直接支持的,所以效率高

答案:2<<3

常见的JDK源码里面HashMap的默认容量16
int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

直接是二进制操作了,表示1左移4位,变成10000,转为10进制也就是16, 直接以二进制形式去运行,效率更高

2、实现变量交换的几种方式

写个方法,传递两个非0的int数值进去,实现变量交换的方式,有几种方式?
方式一:利用算术运算符加减进行变量交换

 public static void swap(int a, int b){System.out.printf("a=%d, b=%d",a,b);
        a = a + b;
        b = a - b ;
        a = a - b;
        System.out.printf("\na=%d, b=%d",a,b);
    }

方式二:利用异或运算来进行变量交换

 public static void swap2(int a, int b){System.out.printf("a=%d, b=%d",a,b);
​
     a = a^b;   // a1 = a^b
     b = b^a;   // b = b^a^b
     a = a^b;   // a = a1^b = a^b^aSystem.out.printf("\na=%d, b=%d",a,b);
 }
 
 //解释:
    a1=a^b
    b=b^a1=b^a^b=a
    //此时a1=a^b  b=a
    a=a1^b=a^b^a=b

二、数据类型

1、java基本数据类型

java八种基本数据类型
byte、short、int、long、float、double、char、boolean
引用数据类型:其他都是引用类型
String和Enum分别是什么类型:引用类型
运算

//定义变量 int i = 5;
return i++;return ++i; 返回结果是什么

2、== 和equals的区别

基本数据类型比较 要用==判断是否相等
引用数据类型: ==比较的是内存地址是否一样,不同对象的内存地址不一样,equals比较的是具体的内容, 也可以让开发者去定义什么条件去判断两个对象是否一样

三、基础语法

1、try-catch-finally异常处理模块的返回值问题

例:下面代码 的try-catch-finally语句,try里面有个return, finally里面也有个return,结果会返回什么?为什么

public static int test1() {
        int a = 1;
        try {
            System.out.println(a / 0);
            a = 2;
        } catch (ArithmeticException e) {
            a = 3;
            return a;} finally {
            a = 4;
        }
        return a;}public static int test2() {
        int a = 1;
        try {
            System.out.println(a / 0);
            a = 2;
        } catch (ArithmeticException e) {
            a = 3;
            return a;} finally {
            a = 4; 
            return a;
        }
​
​
    }

答案:
在执行try、catch中的return之前一定会执行finally中的代码(如果finally存在),如果finally中有return语句,就会直接执行finally中的return方法,所以finally中的return语句一定会被执行的

执行流程:finally执行前的代码里面有包含return,则会先确定return返回值,然后再执行finally的代码,最后再执行return

2、新特性考察 try-with-resource 知识点

JDK7之后的写法,JDK9⼜进⾏了改良,但是变化不⼤,记住下⾯的写法即可
需要关闭的资源只要实现了java.lang.AutoCloseable,就可以⾃动被关闭
try()⾥⾯可以定义多个资源,它们的关闭顺序是最后在try()定义的资源先关闭

 try (
FileInputStream fis = new FileInputStream("/Users/xdclass/Desktop/test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("/Users/xdclass/Desktop/copy.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
      ) {
            int size;
            byte[] buf = new byte[1024];
            while ((size = bis.read(buf)) != -1) {
                bos.write(buf, 0, size);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

四、文件API和递归

例:代码编写需求: 找出某目录下的所有子目录以及子文件并打印到控制台上

public static void main(String[] args) {//找出某目录下的所有子目录以及子文件并打印到控制台上
        List<String> paths = new ArrayList<>();getAllFilePaths(new File("/Users/xdclass/Desktop/demo"),paths);for(String path : paths){
            System.out.println(path);
        }
​
​
    }private static void getAllFilePaths(File filePath, List<String> paths) {File[] files =  filePath.listFiles();
        if(files == null){
            return;
        }
        for(File f : files){
            if(f.isDirectory()){
                paths.add(f.getPath());
                getAllFilePaths(f,paths);
            }else{
                paths.add(f.getPath());
            }
        }
    }

五、新版javase字符串

1、常量池和堆

问题1: String str = new String(“xdclass.net”); 创建了几个对象?

答案:

创建一个对象:常量池存在,则直接new一个对象;

创建两个对象:常量池不存在,则在常量池创建一个对象,也在堆里面创建一个对象

问题2: 下面是比较什么?输出结果是什么?为什么是这样的结果?

String str1= new String("cymcloud.work"); 
String str2= "cymcloud.work"; 
String str3= "cymcloud.work"; 
System.out.println(str1 == str2) //false 
System.out.println(str2 == str3) //true

答案:
比较引用的内存地址是否一样
第一个是false: new 创建新的对象会开辟新的空间,所以地址不一样
第二个是true:都是从常量池里面获取,“xdclass.net” 存在于常量池中

问题3: 写出下面代码的各个结果?如果需要两个都为true,应该怎么修改?

String s1 = "cymcloud";String s2 = s1 + ".work";  //变量 + 常量 = 来自堆String s3 = "cymcloud" + ".work";  //常量 + 常量 = 来自常亮池System.out.println(s2 == "cymcloud.work");System.out.println(s3 == "cymcloud.work");

答案

第一条语句打印的结果为false, s2 = s1 + “.net”, //变量+常量=堆
构建了一个新的string对象,并将对象引用赋予s2变量,常量池中的地址不一样,但是值一样。

第二条语句打印的结果为true,javac编译可以对【字符串常量】直接相加的表达式进行优化,不用等到运行期再去进行加法运算处理,而是直接将其编译成一个这些常量相连的结果.

如果需要第一个输出为true,只需要把变量改为常量即可 fianl String s1 = “xdclass”;
不管是new String(“XXX”)和直接常量赋值, 都会在字符串常量池创建.只是new String(“XXX”)方式会在堆中创建一个对象去指向常量池的对象, 普通的常量赋值是直接赋值给变量

2、常用字符串相关构建类的使用和区别

问:String、StringBuffer与StringBuilder的区别?分别在哪些场景下使用?

答案:

三者都是final, 不允许被继承
在本质都是char[]字符数组实现
String、StringBuffer与StringBuilder中,String是不可变对象,另外两个是可变的

StringBuilder 效率更快,因为它不需要加锁,不具备多线程安全
StringBuffer里面操作方法用synchronized ,效率相对更低,是线程安全的;
使用场景:
操作少量的数据用String,但是常改变内容且操作数据多情况下最好不要用 String ,因为每次生成中间对象性能会降低
单线程下操作大量的字符串用StringBuilder,虽然线程不安全但是不影响
多线程下操作大量的字符串,且需要保证线程安全 则用StringBuffer

六、面向对象思想OOP

例:面向对象的四大特性是?分别解释下。
答案:
抽象
关键词abstract声明的类叫作抽象类,abstract声明的⽅法叫抽象⽅法
⼀个类⾥包含了⼀个或多个抽象⽅法,类就必须指定成抽象类
抽象⽅法属于⼀种特殊⽅法,只含有⼀个声明,没有⽅法体
抽象支付
pay(金额,订单号),默认实现是本地支付,微信支付,支付宝支付,银行卡支付
封装
封装是把过程和数据包围起来,对数据的访问只能通过已定义的接⼝即⽅法
在java中通过关键字private,protected和public实现封装。
封装把对象的所有组成部分组合在⼀起,封装定义程序如何引⽤对象的数据,
封装实际上使⽤⽅法将类的数据隐藏起来,控制⽤户对类的修改和访问数据的程度。 适当的
封装可以让代码更容易理解和维护,也加强了代码的安全性
类封装
⽅法封装

继承
⼦类继承⽗类的特征和⾏为,使得⼦类对象具有⽗类的⽅法和属性,⽗类也叫基类,具有公共的⽅法和属性
动物<-猫
动物<-狗

  abstract class AbsPay{}
       
       WeixinPay extends AbsPay{
           
       }
       
       AliPay extends AbsPay{
           
       }
       

多态
同⼀个⾏为具有多个不同表现形式的能⼒
优点:减少耦合、灵活可拓展
⼀般是继承类或者重写⽅法实现

七、新版javase接口

Overload和Override的区别?
重载Overload:
表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同,参数个数或类型不同
重写Override:
表示子类中的方法可以与父类中的某个方法的名称和参数完全相同
接口是否可以继承接口?接口是否支持多继承?类是否支持多继承?接口里面是否可以有方法实现?
接⼝⾥可以有静态⽅法和⽅法体
接⼝中所有的⽅法必须是抽象⽅法(JDK8之后就不是)
接⼝不是被类继承了,⽽是要被类实现
接⼝⽀持多继承, 类不⽀持多个类继承
⼀个类只能继承⼀个类,但是能实现多个接⼝,接⼝能继承另⼀个接⼝,接⼝的继承使⽤extends关键字,和类继承⼀样
是否了解JDK8里面接口新特性
interface中可以有static⽅法,但必须有⽅法实现体,该⽅法只属于该接⼝,接⼝名直接调⽤ 该⽅法
接⼝中新增default关键字修饰的⽅法,default⽅法只能定义在接⼝中,可以在⼦类或⼦接⼝ 中被重写default定义的⽅法必须有⽅法体
⽗接⼝的default⽅法如果在⼦接⼝或⼦类被重写,那么⼦接⼝实现对象、⼦类对象,调⽤该 ⽅法,以重写为准
本类、接⼝如果没有重写⽗类(即接⼝)的default⽅法,则在调⽤default⽅法时,使⽤⽗类(接口) 定义的default⽅法逻辑。

八、java集合框架List

1、Vector和ArrayList、LinkedList联系和区别和分别的使用场景。

答案:

线程安全

ArrayList:底层是数组实现,线程不安全,查询和修改非常快,但是增加和删除慢
LinkedList: 底层是双向链表,线程不安全,查询和修改速度慢,但是增加和删除速度快
Vector: 底层是数组实现,线程安全的,操作的时候使用synchronized进行加锁
使用场景

Vector已经很少用了
增加和删除场景多则用LinkedList
查询和修改多则用ArrayList

2、ArrayList实现线程安全的几种方式

方式一:自己写个包装类,根据业务一般是add/update/remove加锁

方式二:Collections.synchronizedList(new ArrayList<>()); 使用synchronized加锁

方式三:CopyOnWriteArrayList<>() 使用ReentrantLock加锁

3、CopyOnWriteArrayList和 Collections.synchronizedList实现线程安全的区别,以及使用场景。

CopyOnWriteArrayList:执行修改操作时,会拷贝一份新的数组进行操作(add、set、remove等),代价十分昂贵,在执行完修改后将原来集合指向新的集合来完成修改操作,源码里面用ReentrantLock可重入锁来保证不会有多个线程同时拷贝一份数组

场景:读高性能,适用读操作远远大于写操作的场景中使用(读的时候是不需要加锁的,直接获取,删除和增加是需要加锁的, 读多写少)

Collections.synchronizedList:线程安全的原因是因为它几乎在每个方法中都使用了synchronized同步*锁

场景:写操作性能比CopyOnWriteArrayList好,读操作性能并不如CopyOnWriteArrayList

4、CopyOnWriteArrayList的设计思想以及缺点

答案
设计思想:读写分离+最终一致

缺点:内存占用问题,写时复制机制,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象,如果对象大则容易发生Yong GC和Full GC

5、ArrayList的扩容机制

注意:JDK1.7之前ArrayList默认大小是10,JDk1.7之后是0

未指定集合容量,默认是0,若已经指定大小则集合大小为指定的;
当集合第一次添加元素的时候,集合大小扩容为10
ArrayList的元素个数大于其容量,扩容的大小= 原始大小+原始大小/2

//调试代码List<String> list = new ArrayList<>();for(int i=0;i<10;i++){
    list.add(""+i);
}
System.out.println(list.size());
​
list.add("xdclass.net");System.out.println(list.size());

6、设计一个简单的ArrayList【需要包含 构造函数(有参和无参)、add(obj)、 扩容机制】

 //计算容量+确保容量
    private void ensureCapacityInternal(int minCapacity){//如果是初次扩容,则使用默认的容量
        if(elementData == EMPTY_ELEMENT_DATA){
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }//是否需要扩容,需要的最少容量大于现在数组的长度则要扩容
        if(minCapacity - elementData.length > 0){
            int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity>>1);//如果新容量 < 最小容量, 则讲最新的容量赋值给新的容量
            if(newCapacity - minCapacity < 0){
                newCapacity = minCapacity;
            }//创建新数组
            Object[] objects = new Object[newCapacity];//将旧的数组复制到新的数组里面
            System.arraycopy(elementData,0, objects,0,elementData.length);//修改引用
            elementData = objects;}}

7、设计一个简单的ArrayList【remove(index)、get(index) 、indexOf(o) ,set(int index,Object obj)】

package arrayList;

import java.io.Serializable;

/**
 * 设计一个简单的arrayList 需要包含有参,无参,add(obj),扩容机制
 *
 * @author Cym
 * @date 2021/8/19
 */
public class MyArrayList implements Serializable {

    /**
     * 使用这个字段,来判断当前集合类是否被并发修改,即迭代器并发修改的fail-fast机制
     */
    private transient int modCount = 0;
    /**
     * 第一次扩容的容量
     */
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * 用于初始化空的数组
     */
    private static final Object[] EMPTY_ELEMENT_DATA = {};
    /**
     * 实际存储的元素    transient->防止序列化
     */
    transient Object[] elementData;

    /**
     * 数组实际的长度
     */
    private int size;

    public MyArrayList() {
        this.elementData = EMPTY_ELEMENT_DATA;
    }

    public MyArrayList(int initCapacity) {
        if (initCapacity > 0) {
            this.elementData = new Object[initCapacity];
        } else if (initCapacity == 0) {
            this.elementData = EMPTY_ELEMENT_DATA;
        } else {
            throw new IllegalArgumentException("参数不合法");
        }
    }

    public void add(Object element) {

        //判断容量
        ensureCapcityInternal(size + 1);
        //使用下标赋值,尾部插入
        elementData[size++] = element;

    }

    /**
     * 计算容量 +  确保容量
     *
     * @param minCapacity
     */
    private void ensureCapcityInternal(int minCapacity) {

        modCount++;
        //如果是初次扩容,则使用默认的容量
        if (elementData == EMPTY_ELEMENT_DATA) {
            minCapacity = Math.max(minCapacity, DEFAULT_CAPACITY);
        }
        //是否需要扩容。需要的最少容量大于数组现在的长度则需要扩容
        if (minCapacity > elementData.length) {

            int oldCapacity = elementData.length;

            int newCapacity = oldCapacity + (oldCapacity >> 1);

            //如果新容量 < 最小容量,则将最新的容量赋值给新的容量
            if (newCapacity < minCapacity) {
                newCapacity = minCapacity;
            }

            //创建数组
            Object[] objects = new Object[newCapacity];

            //将旧的数据复制到新的数组里面
            System.arraycopy(elementData, 0, objects, 0, elementData.length);

            //修改引用
            elementData = objects;
        }

    }

    /**
     * 通过下标获取对象
     */
    public Object get(int index) {
        rangeCheck(index);
        return elementData[index];
    }

    /**
     * 更新对象
     *
     * @param index
     * @param object
     * @return
     */
    public Object set(int index, Object object) {
        rangeCheck(index);
        Object oldObject = elementData[index];
        elementData[index] = object;
        return oldObject;
    }

    /**
     * 判断对象所在位置
     *
     * @param object
     * @return
     */
    public int indexOf(Object object) {

        if (object == null) {
            for (int i = 0; i < size; i++) {
                if (elementData[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (elementData[i].equals(object)) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 删除数组元素
     * @param index:下标
     * @return 删除后的数组对象
     */
    public Object remove(int index) {
        rangeCheck(index);
        //用于并发判断
        modCount++;
        Object oldValue = elementData[index];

        //计算删除的位置后面还有几个元素
        int numMoved = size - index - 1;
        System.arraycopy(elementData, index + 1, elementData, index, numMoved);

        //最后一位滞空
        elementData[--size] = null;
        return oldValue;
    }

    public int size() {
        return this.size;
    }

    /**
     * 检查数组下标是否合法
     *
     * @param index
     */
    private void rangeCheck(int index) {
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException("数组下标越界");
        }
    }


}


总结

java基础知识。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RobertTeacher

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值