美团一面被问到Java基础,心里暗喜:你怎么敢的呀?

文章目录

一、Java基础篇

1. 1、Java语言有哪些特点

  1. 简单易学、有丰富的类库

  2. 面向对象(ava最重要的特性,让程序耦合度更低,内聚性更高)

  3. 与平台无关性(JVM是Java跨平台使用的根本)

  4. 安全可靠

  5. 支持多线程和网络编程

1. 2、面向对象和面向过程的区别

面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现, 然后在使用的时候一一调用则可。性能较高,所以单片机、嵌入式开发等-一般采用面向过程开发

面向对象:把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。但是性能上来说,比面向过程要低。

1. 3、八种基本数据类型的大小以及他们的封装类

基本类型大小(字节)默认值封装类
byte1(byte)0Byte
short2(short)0Short
int40Integer
long80LLong
float40.0fFloat
double80.0dDouble
boolean-falseBoolean
char2\u000(null)Character

注意:

  1. int是基本数据类型,Integer是int的封装类, 是引用类型。int默认值是0,而Integer默认值是null,所以Integer能区分出0和nulI的情况。一旦java看到nul,就知道这个引用还没有指向某个对象,再任何弓|用使用前,必须为其指定一个对象, 否则会报错。

  2. 基本数据类型在声明时系统会自动给它分配空间,而引用类型声明时只是分配了引用空间,必须通过实例化开辟数据空间之后才可以赋值。数组对象也是一个引用对象, 将一个数组赋值给另一个数组时只是复制了一个引用,所以通过某一个数组所做的修改在另-一个数组中也看的见。

  3. 虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语 言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。使用int的原因是,对于当下32位的处理器(CPU) 来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。

1.4、标识符的命名规则

  • 命名规则(硬性要求)

    1. 标识符可以包含英文字母,0-9的数字,$以及_
    2. 标识符不可以以数字开头
    3. 标识符不可以是关键字
  • 命名规范(非硬性要求)

    1. 类名规范:首字母大写,后面每个单词首字母大写,简称大驼峰式
    2. 变量名规范:首字母小写,后面每个单词的首字母大写,简称大驼峰式
    3. 方法名规范:同变量名

1. 5、instanceof关键字的作用

instanceof严格来说式Java中的一个双目运算符,用来测试一个对象是否为一个对象的实例

1. 6、Java自动装箱于拆箱

  • 装箱就是自动将基本数据类型转换为包装类型(int->Integer),调用的方法式Integer的valueOf(int)方法
  • 拆箱就是自动将包装类型转化为基本数据类型(Integer->int),调用的方法为Integer的intValue方法

面试题1:以下的代码会输出什么

public class Main{
    public static void main(Sttring[] args){
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

输出的结果为:

image-20201006083801821

为什么会出现这样的结果?输出结果表明i1和i2指向的是同-一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:

 public static Integer valueOf(int i){
            if(i >= -128 && i<=IntegerCache.high)
                return IntegerCache.chche[i+128];
            else
                return new Integer(i)
        }

其中IntegerCache类的实现为:

  private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候, 如果数值在[-128,127]之间, 便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。

面试题2:以下的代码会输出什么

  public static void main(String[] args) {
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;

        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }

输出结果如下:

image-20201006140301132

**原因:**在某个范围内的整型数值的个数是有限的,而浮点数却不是

1.7、重写和重载的区别

1.7 .1、重写(Override)

​ 从字面上看,重写就是重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下,对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。

重写总结

  1. 发生在父类和子类之间
  2. 方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)必须相同
  3. 访问修饰符的限制一定要大于被重写的方法的访问问修饰符(public>protected>deault>private)
  4. 重写方法一定不能抛出新的检查异常或者是比被重写方法申明更加宽泛的检查型异常

1.7.2、重载(Overload)

​ 在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载

重载总结
  1. 重载(Overload)是一个类中多态性的一种表现形式
  2. 重载要求同名方法的参数列表不同(参数类型不同、参数个数不同甚至是参数顺序不同)
  3. 重载的时候,返回值类型可以相同也可以不同

1.8、equals与==的区别

  1. ==: 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)。
  2. equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
    - 情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
    - 情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

1.9、Hashcode的作用

​ hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

1.10、String、StringBuffer和StringBuilder的区别

1.10.1、String

​ String是只读字符串,它并不是基本数据类型,而是一个对象。 从底层源码来看是一个final类型的字符数组,所弓|用的字符串不能被改变,一经定义, 无法再增删改。每次对String的操作都会生成新的String对象。

​ String 类中使用 final 关键字修饰字符数组来保存字符串,private final char[] value,所以 String 对象是不可变的。

​ 在 Java 9 之后,String 类的实现改用 byte 数组存储字符串, private final byte[] value

1.10.2、StringBuffer和StringBuilder

​ StringBuffer和StringBuilder他们两都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到,他并没有用final来修饰,所以是可变的:

/**
*  The value is used for character storage
**/
char[] value;

​ 他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。另外StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

1.11、Array、ArrayList和linkedList的区别

1.11.1、Array

Array (数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。

​ Array获取数据的时间复杂度是0(1),但是要删除数据却是开销很大,因为这需要重排数组中的所有数据,(因为删除数据以后,需要把后面所有的数据前移)

缺点:数组初始化必须指定初始化的长度,否则报错

1.11.2、ArrayList

List-是-个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承Collection。他有两个重要的实现类:ArrayList和LinkedList

​ ArrayList可以看作是一个能够自动增长容量的数组,ArrayList的toArray方法返回一个数组,asList方法返回一个列表,ArrayList的底层的实现是Array,数组扩容实现

1.11.3、LinkedList

LinkList是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.当然,这些对比都是指数据量很大或者操作很频繁。

1.12、HashMap和HashTable的区别

  1. 两者的父类不同

    HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。 不过它们都实现了同时实现.了map、Cloneable (可复制)、Serializable (可序列化)这三个接口。

  2. 对外提供的接口不同

​ Hashtable比HashMap多提供了elments()和contains()两个方法。elments()方法继承自Hashtable的父类Dictionnary。elements() 方法用于返回此Hashtable中的value的枚举。

​ contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()-致。事实上,contansValue()就只是调用了一下contains() 方法。

  1. 对null的支持不同

Hashtable:key和value都不能为null。

HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性,可以有多个key值对应的value为null。

  1. 安全性不同

​ HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。

​ Hashtable是线程安全的,它的每个方法上都有synchronized关键字,因此可直接用于多线程中。

​ 虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,这样设计是合理的,因为大部分的使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap.

​ ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。

  1. 初始容量和每次扩容大小不同
  2. 计算hash值的方法不同

1.13、Collection包结构与Collections的区别

1.13.1、Collection

Collection是集合类的上级接口,子接口有Set、List、 LinkedList. ArrayList、 Vector、 Stack;

1.13.2、Collections

​ Collections是集合类的一个帮助类,它包含有各种有 关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。此类不能实例化,就像-个工具类,服务于Java的Collection框架。

1.14、Java的四种引用:强弱软虚

1.14.1、强引用

​ 强引用是我们平常中使用最多的引用,强引用再程序内存不足(OOM)的时候也不会被回收,使用方式:

String str = new String("str");

1.14.2、软引用

​ 软引用在程序内存不足时,会被回收,使用方式:

//注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的
//这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));

​ 可用场景:创建缓存的时候,把创建的对象放进缓存中,当内存不足时,JVM会回收早先创建的对象。

1.14.3、弱引用

​ 弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:

WeakReference<String> wrf = new WeakReference<String>(str);

​ **可用场景:**java源码中的java.util.weakHashMap中的key就是使用弱引用,我的理解就是,一旦我不需要某个引用,JVM会自动,JVM会自动帮我处理它,这样我就不需要做其他操作。

1.14.4、虚引用

​ 虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入ReferenceQueue中。注意哦,其它引用是被JVM回收后才被传入ReferenceQueue中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有ReferenceQueue

PhantomReference<String> prf = new PhantomReference<String>(new String("str"))new ReferenceQueue<>());

可用场景:对象销毁前的一些操作,比如说资源释放等,object.finalize()虽然也可以做这类动作,但是这个方式即不安全又低效

上诉所说的几类引用,都是指对象的引用,而不是指Reference的四个子类(SoftReference等)

1.15、String str="i"与 String str=new String(“i”)一样吗?

​ 不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

1.16、如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba//
//StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba

1.17、String 类的常用方法都有那些

  • indexOf():返回指定字符的索引
  • charAt():返回指定索引处的字符
  • replace():字符串替换
  • trim():去除字符串两端空白
  • split():分割字符串,返回一个分割后的字符串数组
  • getBytes():返回字符串的 byte 类型数组
  • length():返回字符串长度
  • toLowerCase():将字符串转成小写字母
  • toUpperCase():将字符串转成大写字符
  • substring():截取字符串
  • equals():字符串比较

1.18、抽象类必须要有抽象方法吗,和普通类的区别

​ 不需要,抽象类不一定非要有抽象方法

​ 普通类和抽象类的区别如下:

  1. 普通类不能包含抽象方法,抽象类可以包含抽象方法
  2. 抽象类不能直接实例化,普通类可以直接实例化

1.19、接口和抽象类的区别

  1. 接口中所有的方法隐含的都是抽象的,但是抽象类中可以同时包含抽象方法和普通方法以及静态常量
  2. 类可以实现很多个接口,但是只能继承一个抽象类
  3. 类如果要实现一个接口,那么他必须要实现接口声明的所有方法,但是类可以不实现抽象类中的所有方法,但是这个类必须是抽象类
  4. 接口中不存在构造方法,因为接口的成员变量都是static final变量,是在编译的时候就完成了初始化操作了,无需通过构造方法来进行初始化操作,而抽象类必须有构造方法

1.20、抽象类能使用 final 修饰吗?

​ 不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类

1.21、Java 中 IO 流分为几种

  1. 按功能来分:输入流(input)、输出流(output)。
  2. 按类型来分:字节流和字符流。
    字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。

1.22、BIO、NIO、AIO 有什么区别?

  • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低
  • NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO的操作基于事件和回调机制。

1.23、Java创建对象有几种方式?

  1. new创建新对象
  2. 通过反射机制
  3. 采用clong机制
  4. 通过序列化机制

1.24、两个不相等的对象有相同的hashcode时如何处理

当产生hash冲突时,两个不相等的对象就会有相同的hashcode值,当hash冲突产生的时候,解决办法有三种:

  1. 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成的一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储
  2. 开放地址法:一旦发生了冲突,就会去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并且记录存入
  3. 再哈希:又叫双哈希法,有多个不同的Hash函数,当发生冲突的时候,使用第二个,第三个,利用哈希函数计算地址,直到再无冲突

1.25、final有哪些用法?

  1. 被final修饰的类不可以被继承
  2. 被final修饰的方法不可以重写
  3. 被final修饰的变量不可以被改变,如果修饰引用,那么表示引用不可变,引用指向的内容可变
  4. 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
  5. 被final修饰的常量,再编译阶段会存入常量池

1.26、static的用法

  1. static有两个基本的用法:静态变量和恶静态方法,被static所修饰的变量/方法读书与静态1资源,类实例所共享
  2. static也用于静态块,多用于初始化操作
  3. static也多用于修饰内部类,被称为静态内部类
  4. 可用于静态导包,指定导入某个类中的静态资源,并且不需要使用此类名,可以直接使用资源名

1.27、a=a+b与a+=b的区别

+=操作符会进行隐式自动类型转换,a+=b隐式地将加的操作的结果类型强制转换为持有结果类型,而a=a+b则不会自动进行类型转换

1.28、try catch finally 中,try里有return,finally还会执行吗

​ 执行。总结如下:

1、不管有没有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

1.29、hashCode()和equals()的区别

  1. 性能:重写的hash()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只需要产生一个hash值进行比较就可以了,效率很高

  2. 可靠性:hashCode并不是完全可靠的,有时候不同的对象他们产生的hashCode也会一样

    (1)equals相等的两个对象他们的hashCode肯定相等,也就是说equals()是绝对可靠的

    (2)hashCode相等的两个对象他们的equals()不一定相等,也就是说hashCode不是绝对可靠的

1.30、你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode 方法?

​ 是为了提高效率,采取重写hashcode方法,先进行hashcode比较,如果不同,那么就没必要在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的。

1.31、描述深拷贝和浅拷贝

  1. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
  2. 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

1590376017060

1.32、说说&和&&的区别

​ &和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。

​ &&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式。

​ &还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作。

1.33、Object的常用方法

public final native Class<?> getClass()//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。

public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。

protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。

public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。

public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。

public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。

public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。

public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。

public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念

protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

悟空打码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值