面试汇总——JavaSE——Java基础(二)

根据牛客网的面试经验的题进行的汇总,里面的内容是看别人的博客或者其东西进行汇总的,将这些的知识总结一下,方便自己查看和复习用
牛客网

Java基础(二)

JAVA notation
请你谈谈大O符号(big-O notation)并给出不同数据结构的例子

表示随着数据结果元素的增加,在最坏场景下的最好情况

Big O notation大零符号一般用于描述算法的复杂程度,比如执行的时间或占用内存(磁盘)的空间等,特指最坏时的情形。

补充【例子】

  • 常量阶(O(1)):代码片段执行时间不随数据规模n的增加而增加,即使执行次数非常大,只要与数据规模无关,都算作常量阶【n=100,此时只执行1次】
  • O(n):成线性增加【n=100,此时执行100次】
  • 按量级递增排序:常量阶O(1)< 对数阶O(logn) < 线性阶O(n) < 线性对数阶O(nlogn) < 平方阶O(n²)…立方阶O(n³)…k方阶 < 指数阶O(2n次方) < 阶乘阶O(n!)

时间复杂度

  • 加法原则【找最大的复杂度】【2n=+2,其中的两个2都可以省略,复杂度为f(n)】
  • 乘法原则【两个复杂度的乘积】

空间复杂度

  • 计算开辟的空间大小
Array
请你讲讲数组(Array)和列表(ArrayList)的区别?什么时候应该使用Array而不是ArrayList?

可以将 ArrayList想象成一种“会自动扩增容量的Array”

Array数组

Array数组可以存放基本数据类型和对象,效率高,但长度固定

Array名称本身实际上是个reference,指向heap之内得某个实际对象

【代码测试】可以利用Arrays工具类进行操作【没有删除】sort()、parallelSort()、swap()、binarySearch()、fill()、copyOf()、hsshCode()…

ArrayList

ArrayList只能存放对象,指向堆内存的引用时Object类型,长度可以变化,但效率低

数组扩容是对ArrayList效率影响比较大的一个因素

【代码测试】add()、remove()、toArray()、replaceAll()、sort()、indexOf()…

Java值传递和引用传递
请你解释什么是值传递和引用传递?

一般认为,java内的传递都是值传递【代码测试】

值传递

在操作时,是将原对象进行拷贝,对拷贝以后的对象操作,不会改变原对象的值

引用传递

是将原对象的引用进行拷贝,改变拷贝对象的值,会改变原对象的值

JAVA数据类型
请你讲讲Java支持的数据类型有哪些?什么是自动拆装箱?

基本数据类型:byte、char、short、int、long、float、double、boolean

引用数据类型:String、对象…

拆箱:包装类->基本数据类型 intValue、自动拆箱

装箱:基本数据类型->包装类 包装类的构造器、valueOf、自动装箱

Java中除了float和double的其他基本数据类型,都有常量池

基本数据类型对应的包装类父类
byteBytejava.lang.Number
shortShortjava.lang.Number
intIntegerjava.lang.Number
longLongjava.lang.Number
charCharacterjava.lang.Object
floarFloatjava.lang.Number
doubleDoublejava.lang.Number
booleanBooleanjava.lang.Object
基本数据类型 到 包装类包装类 到 基本数据类型
使用包装类的构造方法调用包装类类的xxxValue()方法
使用包装类内部的valueOf( )方法自动拆箱
自动装箱

补充

包装

  • 所有包装类都是final类型,因此不能创建他们的子类
  • 包装类是不可变类,一个包装类的对象自创建后,他所包含的基本类型数据就不能被改变
  • 在进行判断时,**[-128—127]**在常量池中
Integer i1 = new Integer(12);
Integer i2 = new Integer(12);
System.out.println(i1 == i2);//指向不同堆内存

Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);//指向不同堆内存

Integer i5 = 12;
Integer i6 = 12;
System.out.println(i5 == i6);//在常量池中[-128—127]

Integer i7 = -129;
Integer i8 = -129;
System.out.println(i7 == i8);//不在常量池中

Integer i9 = new Integer(128);//会自动拆箱
int a = 128;
System.out.println(i9 == a);//两个基本数据类型比较
计算机基础
请你解释为什么会出现4.0-3.6=0.40000001这种现象?

因为计算机无法计算十进制,在进行计算时,会先将十进制转化为二进制进行计算,这个过程出现了误差

请你讲讲一个十进制的数在内存中是怎么存的?

用补码的形式

补充

正数的原反补一样

负数的反码是原码的符号位不变,其余求反;补码是反码+1

Java基础
请你说说Lamda表达式的优缺点

优点

  • 使表达更加简介
  • 可以执行并行计算

缺点

  • 对于新手来说不好理解
  • 若不用并行计算,很多时候计算速度没有比传统的 for 循环快(并行计算有时需要预热才显示出效率优势)
  • 不容易调试

代码测试

pojo

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private int age;
}

普通方法增加

public class TestCommon {

    public static List<User> users() {
        List<User> userList = Arrays.asList(
                new User(1, "aaa", 24),
                new User(2, "bbb", 34),
                new User(3, "ccc", 66),
                new User(4, "ddd", 19)
        );
        return userList;
    }

    //判断年纪大于30岁
    public static List<User> filterAge(List<User> users) {
        users = users();
        ArrayList<User> arrayList = new ArrayList<User>();//用此方法生成ArrayList有那些方法,因为自己都重写了
        for (User user: users) {
            if (user.getAge() >= 30) {
                arrayList.add(user);//用Arrays工具类生成的ArrayList【匿名内部类】没有add()、remove()...
            }
        }
        System.out.println(arrayList);
        return users;
    }

    //增加方法时的代码太多

    public static void main(String[] args) {

        //查询年纪大于30岁的员工
        filterAge(users());
    }
}

策略模式实现增加

//策略模式
public interface Design<T> {
    //此接口定义一个过滤方法,为true过滤,false阻止
    public boolean filter(T t);
}
public class DesignImpl implements Design<User> {

    public boolean filter(User user) {
       return user.getAge() >= 30;
    }

    //只用在此处增加方法代码
}
public class DesignFilter {
    public List<User> filterUser(List<User> list, Design<User> design) {
        ArrayList<User> arrayList = new ArrayList<User>();
        for (User user: list) {
            if (design.filter(user)) {
                arrayList.add(user);
            }
        }
        return arrayList;
    }
}
public class TestDesign {
    public static void main(String[] args) {
        List<User> userList = Arrays.asList(
                new User(1, "aaa", 24),
                new User(2, "bbb", 34),
                new User(3, "ccc", 66),
                new User(4, "ddd", 19)
        );
        DesignFilter filter = new DesignFilter();
        List<User> users = filter.filterUser(userList, new DesignImpl());
        for (User user : users) {
            System.out.println(user);
        }
    }
}

lambda表达式实现

DesignFilter(this.userList, (user) -> user.getAge() >= 30).forEach(System.out::println);
请你说明符号“==”比较的是什么?

基本数据类型

比较的是值

引用类型

比较的是地址,看是否指向同一个对象

补充

基本数据类型,包装类(除了Float、Double)以及声明式的String都放在常量池中

public boolean equals(Object var1) {
    return this == var1;
}

本质上equals也是实现“==”,但是许多类会重写equals(),所以才会有值的比较,如果没有重写equals()那么比较的还是地址

请你解释Object若不重写hashCode()的话,hashCode()如何计算出来的?

如果不重写就是使用Object类中的,是由c/c++编写出来的

public native int hashCode();//由c/c++编写

说白了就是判断在集合中是否有相同的对象,用equals实现的代价太大,无论何时,对同一个对象调用hashcode()都应该产生同样的值

逻辑上,如果两个对象的equals方法返回是相等的,那么它们的hashcode必须相等;反之不一定成立c。

请你解释为什么重写equals还要重写hashcode?

重写hashcode是为了提高效率

重写后的equals是判断属性值是否一样,但其本质是判断地址,想达到的效果是判断属性值是否一样的前提先判断hashcode是否一样

public boolean equals(Object var1) {
    return this == var1;
}

如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的

如果只重写equals,那么就会出现,不是同一对象但是因为有相同的个别属性(重写个别属性)【就是因为我们重写的那几个属性相同】,equals就会返回true

如果只重写hashcode,就会出现是不同对象【因为经过哈希算法导致hashcode可能相同】,但其特征相同,equals也会返回true,此时只是单纯的比较了地址

hashCode是所有java对象的固有方法,如果不重写的话,返回的实际上是该对象在jvm的堆上的内存地址,而不同对象的内存地址肯定不同,所以这个hashCode也就肯定不同了。如果重写了的话,由于采用的算法的问题,有可能导致两个不同对象的hashCode相同

  • hashcode相等equals不一定相等
  • hashcode不相等equals一定不相等
  • equals相等hashcode一定相等
请你介绍一下map的分类和常见的情况

Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖),但允许值重复

HashMap

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {}

HashMap最多只允许有一个记录的键为null,可以允许多个记录的值为null

HashMap 是一个最常用的Map,它根据键的Hashcode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的

HashMap非线程安全,不支持线程同步,如果同时有多个线程操作HashMap,可能会导致数据不一致

如果想要线程安全可以通过Collections.synchronizedMap(…){…}获得

补充

JDK1.8之后有原来的“链表+数组”变成了“链表+数组+红黑树”

当链表的节点数大于8时,会生成红黑树,红黑树又前后两个节点(prev、next)链表只用一个节点(next)

源码中对红黑树的查找,当查找的目标节点的hash值小于p节点,则向p的左边遍历,否则向p的右边遍历;key值的原来一样

Hashtable

public class Hashtable<K, V> extends Dictionary<K, V> implements Map<K, V>, Cloneable, Serializable {}

不允许记录的键或者值为空

持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢

TreeMap

public class TreeMap<K, V> extends AbstractMap<K, V> implements NavigableMap<K, V>, Cloneable, Serializable {}

线程不安全

能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的

LinkedHashMap

public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V> {}

保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时用带参数,按照应用次数排序

LinkedHashMap的遍历速度只和实际数据有关,和容量无关;而HashMap的遍历速度和他的容量有关

Java8
你知道Java8的新特性吗,请简单介绍一下
  • lambda表达式:
  • 方法引用:
  • stream API:
  • 默认方法:允许在接口中实现一个类
  • Optional 类:
  • Date Time API
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值