首页IT科技java面试 csdn(一天十道Java面试题—-第一天(面向对象——-》ArrayList和LinkedList))

java面试 csdn(一天十道Java面试题—-第一天(面向对象——-》ArrayList和LinkedList))

时间2025-06-20 08:22:12分类IT科技浏览4216
导读:这里是参考B站上的大佬做的面试题笔记。大家也可以去看视频讲解!!!...

这里是参考B站上的大佬做的面试题笔记             。大家也可以去看视频讲解!!!

1             、面向对象

1.1                   、 什么是面向对象?

对比面向过程      、是两种不同的处理问题的角度

面向过程更注重事情的没一个步骤及顺序            ,面向对象更注重事情有哪些参与者(对象)                   ,及各自需要做什么                   。

例如:洗衣机洗衣服

面向过程会将任务拆解成一系列的步骤(函数)       ,1      、打开洗衣机---->2                   、放衣服------>3            、放洗衣粉---->4      、清洗------>5                   、烘干 面向对象会拆出人和洗衣机两个对象:人:打开洗衣机            ,放衣服                  ,放洗衣粉      。洗衣机:清洗            、烘干

从以上例子可以看出:面向过程比较直观高效       ,而面向对象更易于复用      ,扩展和维护      。

1.2 、面向对象

封装

封装的意义                  ,在于明确标识出允许外部使用的所有成员函数和数据项             ,内部细节对外部调用透明      ,外部调用无需修改或者关心内部实现                   。

1                   、javabean的属性私有                  ,提供get/set对外访问             ,因为属性的赋值或者获取逻辑只能由javabean本身决定,而不能由外部胡乱修改            。

2                  、orm框架:操作数据库                  ,我们不需要关心链接时如何建立的                   ,sql时如何执行的,只需要引入mybatis            ,调方法即可      。

继承

继承基类的方法                   ,并做出自己的改变或扩展 子类共性的方法或者属性直接使用父类的       ,而不需要自己再定义            ,只需扩展自己个性化的

多态

基于对象所属类的不同                  ,外部对同一个方法的调用       ,实际执行的逻辑不同                   。 继承      ,方法重写                  ,父类引用指向子类对象 父类类型 变量名 = new 子类对象; 变量名.方法名();

无法调用子类特有的功能

2、JDK             、JRE                  、JVM区别和联系

JDK:

java Develpment Kit java 开发工具

JRE:

java Runtime Environment java 运行时环境

JVM:

java Virtual Machine java 虚拟机

3      、==和equals

对比的时栈中的值             ,基本数据类型是变量值      ,引用类型是堆中内存对象的地址 equals:object中默认也是采用比较                  ,通常会重写 String类中被复写的equals()方法其实是比较两个字符串的内容

举例

@Test void test(){ String str1 = "hello"; String str2 = new String("hello"); String str3 = str2;//引用传递 System.out.println("123"); System.out.println("(str1 == str2):"+(str1 == str2));//false System.out.println("(str1 == str3):"+(str1 == str3));//false System.out.println("(str2 == str3):"+(str2 == str3));//true System.out.println("str1.equals(str2):"+str1.equals(str2));//true System.out.println("str1.equals(str3):"+str1.equals(str3));//true System.out.println("str2.equals(str3):"+str2.equals(str3));//true }

4             、final

4.1                   、简述final作用 最终的

修饰类:表示类不可被继承 修饰方法:表示方法不可被子类覆盖             ,但是可以重载 修饰变量:表示变量一旦被赋值就不可以更改它的值            。

(1)       、 修饰成员变量

如果final修饰的类变量,只能在静态初始化块中指定初始值或声明该类变量时指定初始值 如果final修饰的是成员变量                  ,可以在非静态初始化块                   ,声明该变量或者构造器中执行初始值。

(2)       、修饰局部变量

系统不会为局部变量进行初始化,局部变量必须由程序员显示初始化            ,因此使用final修饰局部变量时                   ,即可以在定义时指定默认值(后面的代码不能对变量再赋值)       ,也可以不指定默认值            ,而在后面的代码中对final变量赋初值(仅一次)

(3)                   、修饰基本类型数据和引用类型数据

如果是基本数据类型的变量                  ,则其数值一旦在初始化之后便不能更改 如果是引用类型的变量       ,则在对其初始化之后便不能再让其指向另一个对象      ,但是引用的值是可以改变的

4.2             、为什么局部内部类和匿名内部类只能访问局部final变量?

首先需要知道的一点是:内部类和外部类是处于同一个级别的                  ,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁                   。

这里就会产生问题:当外部类的方法结束时             ,局部变量就会被销毁      ,但是内部类对象可能还存在(只有没有人再引用它时                  ,才会死亡)                  。这里就出现了一个矛盾:内部类对象访问了一个不存在的变量。为了解决这个问题             ,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后                  ,内部类仍可以访问它                   ,实际访问的是局部变量的copy,这样好像就延长了局部变量的生命周期             。

将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的            ,也就是如果我们再内部类中修改了成员变量                   ,方法中的局部变量也得跟着改变       ,怎样解决问题呢?

就将局部变量设置为final      、对它初始化之后            ,我就不让你再去修改这个变量                  ,就保证了内部类的成员变量和方法的局部变量的一致性       ,这实际上也是一种妥协      ,使得局部变量与内部类内建立的拷贝保持一致性                  。

5                   、String             、StringBuffer、StringBuilder区别及使用场景

String是final修饰的                   、不可变                  、每次操作都会产生新的String对象 StringBuffer和StringBuilder都是在原对象上操作 StringBuffer是线程安全的                  ,StringBuilder线程不安去的 StringBuffer方法都是synchronized修饰的

性能:StringBuilder > StringBuffer > String

场景:经常需要改变字符串内容时使用后面两个

优先使用StringBuilder             ,多线程使用共享变量时使用StringBuffer

6、重载和重写的区别

重载:发生在同一个类中      ,方法名必须相同                  ,参数类型不同             ,顺序不同,方法的返回值和访问修饰符可以不同                  ,发生在编译时期      。 重写:发生在父子类中                   ,方法名,参数列表必须相同            ,返回值范围小于等于父类                   ,抛出的异常范围小于等于父类             。访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法                   。

7             、接口和抽象类

抽象类可以存在普通成员函数       ,而接口中只能存在public abstract方法 抽象类中的成员变量可以是各种类型的            ,而接口中的成员变量只能是public static final 类型的 抽象类只能继承一个                  ,接口可以实现多个      。

接口的设计目的       ,是对类的行为进行约束(更准确的说是一种“有             ”约束      ,因为接口不能规定类不可以有什么行为)                  ,也就是提供一种机制             ,可以强制要求不同的类具有相同的行为      ,它只约束了行为的有无                  ,但不对如何实现行为进行限制      。

而抽象类的设计目的             ,是代码复用                   。当不同的类具有某些相同的行为(记为行为集合),且其中一部分行为的实现方式一致时(A的非真子集                  ,记为B)可以让这些类都派生与一个抽象类                   ,在这个抽象类中实现了B,避免让所有的子类来实现B            ,这就达到了代码复用的目的            。而A减去B的部分                   ,留给各个子类自己实现       ,正是因为A-B在这里没有实现            ,所以抽象类不允许实例化出来(否则当调用到A-B时                  ,无法执行)      。

抽象类是对类本质的抽象       ,表达的是is-a的关系      ,比如:BMW is a car                    。抽象类包含并实现子类的通用特性                  ,将子类存在差异化的特性进行抽象             ,交由子类去实现            。

而接口是对行为的抽象      ,表达的是like a的关系                  ,比如:bird like a aircraft(像飞行器一样飞)             ,但其本质上 is a bird 。接口的核心是定义行为,至于实现主体是谁                  ,是如何实现                   ,接口并不关心                   。

使用场景:当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候            ,用接口

抽象类的能力要远超过接口                   ,但是       ,定义抽象类的代价高            ,因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类                  ,在这个类中       ,你必须继承或编写出其所有子类的所有共性                  。虽然接口在功能上会弱化许多      ,但是它只是针对一个动作的描述                  ,而且你可以在一个类中同时实现多个接口             ,在设计阶段会降低难度

8                  、List和Set

List:有序      、按对象进入的顺序保存对象      ,可重复                  ,允许多个Null元素对象             ,可以使用iterator取出所有元素,在逐一遍历                  ,还可以使用get(int index)获取指定下的元素。 Set:无序             、不可重复                   、最多允许有一个Null元素对象                   ,取元素时只能用iterator接口取得所有的元素,在逐一遍历各个元素             。 **铁汁们      、铁汁们      、铁汁们                   、都看到这里了            、动动可爱的小手      、点个赞呗                   、鼓励一下                  。哈哈哈**

9            、hashcode和equals

hashcode介绍

hashcode()的作用是获取哈希码            ,也称为散列码;它实际上是返回一个int整数      。这个哈希码的作用是确定该对象在哈希表中的索引位置             。hashcode()定义在JDK的Object.java中                   ,java中的任何类都包含有hashcode()函数                   。散列表存储的是键值对(key-value),它的特点是:能根据“键                   ”快速的检索出对应的“值      ”      。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有hashcode

以“HashSet如何检查重复      ”为例子来说明为什么要有hashcode:

对象加入HashSet时       ,HashSet会先计算对象的hashcode值来判断对象加入的位置            ,看该位置是否有值                  ,如果没有、HashSet会假设对象没有重复出现      。但是如果发现有值       ,这时会调用equals()方法来检查两个对象是否真的相同                   。如果两者相同      ,HashSet就不会让其加入操作成功            。如果不同的话                  ,就会重新散列到其他位置      。这样就大大减少了equals的次数             ,相应就大大提高了执行速度                   。

如果两个对象相等      ,则hashcode一定也是相同的            。 两个对象相等                  ,对两个对象分别调用equals方法都返回true 两个对象有相同的hashcode值             ,它们也不一定是相等的 因此                   、equals方法被覆盖过,则hashcode方法也必须被覆盖 hashcode()的默认行为是对堆上的对象产生独特值                  ,如果没有重写hashcode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

10                  、ArrayList和Linkedlist

ArrayList:

基于动态数组                   ,连续内存存储,适合下标访问(随机访问)            ,扩容机制:因为数组长度固定                   ,超出长度存数据时需要新建数组       ,然后将老数组的数据拷贝到新数组            ,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素)                  ,使用尾插法并指定初始容量可以极大提升性能甚至超过LinkedList(需要创建大量的index对象)

LinkedList:

基于链表       ,可以存储在分散的内存中      ,适合做数据插入及删除操作                  ,不适合查询;需要逐一遍历             ,遍历LinkedList必须使用iterator不能使用for循环      ,因为每次for循环体内通过get(i)取得某一元素时都需要对list重新遍历                  ,性能消耗极大。

另外不要试图使用index()等返回元素索引             ,并利用其进行遍历,使用indexof对list进行了遍历                  ,当结果为空时会遍历整个列表
声明:本站所有文章                   ,如无特殊说明或标注,均为本站原创发布                   。任何个人或组织            ,在未征得本站同意时                   ,禁止复制、盗用             、采集                  、发布本站内容到任何网站      、书籍等各类媒体平台                  。如若本站内容侵犯了原著者的合法权益       ,可联系我们进行处理。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
枚举法公式(【TypeScript入门】TypeScript入门篇——枚举(enum))