java1

一、基础

1、面向对象都有哪些特性以及你对这些特性的理解

(1)继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。

(2)封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。

(3)多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。

多态性分为编译时的多态性和运行时的多态性。

方法重载(overload)实现的是编译时的多态性(也称为前绑定)

如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当 A 系统访问 B 系统提供的服务时,B 系统有多种提供服务的方式, 但一切对 A 系统来说都是透明的。

方法重写 (override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做 两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

(4)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

 

2、重载(overload)和重写(override)的区别?重载的方法能否根据返回类型 进行区分?

重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为 重载,重载对返回类型没有特殊的要求;

重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。

方法重载的规则:

1、方法名一致,参数列表中参数的顺序,类型,个数不同。

2、重载与方法的返回值无关,存在于父类和子类,同类中。

3、可以抛出不同的异常,可以有不同修饰符

方法重写的规则:

1、参数列表必须完全与被重写方法的一致,返回类型必须完全与被重写方法的返回类型一致。

2、构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。

3、访问权限不能比父类中被重写的方法的访问权限更低。

4、无论被重写的方法是否抛出异常,重写的方法能够抛出任何非强制异常(UncheckedException,也叫非运行时异常)。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常。

为什么函数不能根据返回类型来区分重载?

因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。

float max(int a, int b);

int max(int a, int b);

当调用 max(1, 2) 时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。

 

3、权限修饰符

4、在 Java 中,如何跳出当前的多重嵌套循环

在最外层循环前加一个标记如 A,然后用 break A;可以跳出多重循环。(应该避免使用带标签的 break 和 continue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用)

 

5、是否可以继承 String

https://blog.csdn.net/SnowDujc/article/details/105083911   这篇博客较详细

String 类是 final 类,不可以被继承。

通过String源码可以看到,String类型的底层是由final修饰的char数组存储。String是引用数据类型

 

String能被设计成不可变类型的一个重要前是因为它是编程语言里面使用频率最高的一种类型。不可变类型带来的好处,体现在四个方面,分别是:缓存,安全,同步和性能。

缓存、性能:在JVM的运行时数据区域里面,有一个专门的字符串常量池用来存储字符串字面量。当第一个字面量声明的时候,它的值会被字符串常量池存储,当s2变量声明的时候,jvm发现常量池已经存在该对象,所以就不会再创建一次,而是直接将一样的内存指针赋值给s2变量,从避免了重复创建对象,节省了内存空间。

由于字符串的不可变性,从而可以让其hashCode也被缓存,在Java里面哈希类数据结构如HashMap, HashTable, HashSet其key用的最多的基本都是String类型,如此一来key的hashCode的也可以在第一次调用之后被缓存,之后直接使用无须重新生成,从而间接的提升访问效率。

安全:如先检查字段是否合法,再继续使用数据。如果String可变,那么攻击者就可以在通过检查之后,再改变字段,那么就会存在安全风险,而不可变性能够避免和减少这一情况。另一方面,如果String是可变的,同时运行的其他线程如果修改这个值,就有可能导致混乱。

同步:由于String类型的不可变性,使得String对象可以安全的在多个线程之间传递和访问。

 

 

6、String 、StringBuilder 、StringBuffer 的区别

 

(1)String 是只读字符串, String 引用的字符串内容是不能被改变的

(2)StringBuffer / StringBuilder 表示的字符串对象可以直接进行修改。

(3)StringBuilder 是 Java5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的, 因为它的所有方法都没有被 synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高。

 

7、抽象类和接口

1、抽象类

如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。(抽象类无法被实例化,也就是我们不能去创建它的对象)

抽象方法
• 抽象方法是一种特殊的方法:它只有声明,而没有具体的实现 
• 抽象方法必须用 abstract 关键字进行修饰
 
1、抽象类中可以定义构造器
2、抽象类中可以有抽象方法和具体方法
3、抽象类中可以定义成员变量
4、抽象类中的成员可以是 private、默认、protected、public
5、有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法
6、抽象类中可以包含静态方法
7、一个类只能继承一个抽象类
2、接口
使用 interface 关键字用来声明一个接口。
1、接口中不能定义构造器
2、方法全部都是抽象方法
3、接口中的成员全都是 public 的
4、接口中定义的成员变量实际上都是常量
5、接口中不能有静态方法
6、一个类可以实现多个接口

从本质上来讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。也就是说,它里面只定义了一些功能并没有实现,所以也不能被实例化。

为什么会有接口?(接口存在的意义) : 因为类只能有一个父类,使用接口我们可以实现多继承这样的逻辑

8、自增自减运算符

自增运算符(++)和自减运算符(--)。

当运算符放在操作数之前时,先自增/减,再赋值;当运算符放在操作数之后时,先赋值,再自增/减。

例如,当“b=++a”时,先自增(自己增加 1),再赋值(赋值给 b);当“b=a++”时,先赋值(赋值给 b),再自增(自己增加 1)。

也就是,++a 输 出的是 a+1 的值,a++输出的是 a 值。用一句口诀就是:“符号在前就先加/减,符号在后就后加/减”。

a = 1     b = ++a  ——> a=2  b=2

a = 1     b = a++  ——> a=2  b=1

a = 1     b = a--    ——> a=0  b=1

a = 1     b = --a    ——> a=0  b=0

 

9、continue、break、和return的区别是什么?

1. continue :指跳出当前的这一次循环,继续下一次循环。

2. break :指跳出整个循环体,继续执行循环下面的语句。

3. return 用于跳出所在方法,结束该方法的运行。return 一般有两种用法:

return;:直接使用 return 结束方法执行,用于没有返回值函数的方法

return value: return一个特定值,用于有返回值函数的方法

 

10、“==”和equals的区别

“==”和equals 最大的区别是
  • “==”是运算符,如果是基本数据类型,则比较存储的值;如果是引用数据类型,则比较所指向对象的地址值。
  • equals是Object的方法,比较的是所指向的对象的地址值,一般情况下,重写之后比较的是对象的值。

10.1 “==”是运算符

1. 如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
2. 如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。

10.2 equals是Object的方法,用来比较两个对象的内容是否相等。

1. equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址值。

2. 一般情况下,类会重写equals方法用来比较两个对象的内容是否相等。比如String类中的equals()是被重写了,比较的是对象的值。

 

11、HashCode和equal方法

https://blog.csdn.net/WinnerBear/article/details/126068645     这篇比较详细明了

在java中有一个共同的父类 Object ,所有对象均隐性继承了此类,其中 equals() 与 hashCode() 就是继承过来的方法。

equals()反映的是对象或变量具体的值,即两个对象里面包含的值,可能是对象的引用,也可能是值类型的值。

而hashCode()是对象或变量通过哈希算法计算出的哈希值。

之所以有hashCode方法,是因为在批量的对象比较中,hashCode要比equals来得快,很多集合都用到了hashCode,比如HashTable。

Java 对于 eqauls 方法和 hashCode 方法是这样规定的:

(1)如果两个对象相同(equals 方法返回 true),那 么它们的 hashCode 值一定要相同;

(2)如果两个对象的 hashCode 相同,它们并不一定相同。

11.1  为什么要重写equals、hashCode方法?

答案:

(1)如果类中不重写方法:

hashCode():属于本地方法,返回的是对象的哈希码值,也称为散列码,实际返回的是一个int的整数。

(根据一定的规则与对象相关信息(例如对象的存储地址,对象的字段等)映射成一个数组,这个数值就被称为散列值。)

equals():用来比较两个对象的地址值是否相等。

(2)如果类中重写方法:

hashCode():返回的是根据对象的成员变量,计算出的一个整数。

equals():比较的是两个对象中成员信息是否相同。

11.2  为什么要有 hashCode?

很多情况下,我们比较对象并不需要比较对象的地址,而是只要是同一个类的不同对象,成员属性值相同,我们就认为是同一个对象。

正常情况下如果hashcode相同,得到相同的数组下标位置,还需要进行equals来比较是否是同一个元素。

如:当你把对象加入 HashSet 时, HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时 也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode, HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同, HashSet 就不会让其加入操作成功。如果不同的 话,就会重新散列到其他位置。

 

12、Java 中异常

12.1 异常分为哪些种类

1、按照异常需要处理的时机分为编译时异常(也叫强制性异常)也叫 CheckedException 和运行时异常 (也叫非强制性异常)也叫 RuntimeException。

只有 java 语言提供了 Checked 异常,Java 认为 Checked 异常都是可以被处理的异常,所以 Java 程序必须显式处理 Checked 异常。

对 Checked 异常处理方法有两种:

(1)当前方法知道如何处理该异常,则用 try...catch 块来处理该异常。

(2)当前方法不知道如何处理,则在定义该方法是声明抛出该异常

2、运行时异常只有当代码在运行时才发行的异常,编译时不需要 try catch。

RuntimeException如除数是 0 和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检 测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。

12.2 error 和 exception 的区别

Error 类和 Exception 类的父类都是 Throwable 类,

Error 类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类 错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

Exception 类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

Exception 类又分为运行时异常(RuntimeException)和受检查的异常(CheckedException ),运行时异 常;ArithmaticException(算数异常),IllegalArgumentException(参数异常),编译能通过,但是一运行就终止了,程序不会处理运行时异常, 出现这类异常,程序会终止。而受检查的异常,要么用 try……catch 捕获,要么用 throws 字句声明抛出,交给它 的父类处理,否则编译不会通过。

12.3 常见的 运行时异常    RuntimeException

(1)java.lang.NullPointerException    空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。

(2)java.lang.ClassNotFoundException    指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序 试图通过字符串来加载某个类时可能引发异常。

(3)java.lang.NumberFormatException    字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。

(4)java.lang.IndexOutOfBoundsException    数组角标越界异常,常见于操作数组对象时发生。

(5)java.lang.IllegalArgumentException    方法传递参数错误。

(6)java.lang.ClassCastException    数据类型转换异常

(7)java.lang.NoClassDefFoundException    未找到类定义错误。

(8)SQLException    SQL 异常,常见于操作数据库时的 SQL 语句错误。

(9)java.lang.InstantiationException    实例化异常。

(10)java.lang.NoSuchMethodException    方法不存在异常。

12.4 常见的 运行时异常    throw 和 throws 的区别

throw:

(1)throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。

(2)throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常。

throws:

(1)throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。

(2)throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。

(3)throws 表示出现异常的一种可能性,并不一定会发生这种异常。

12.5 final、finally、finalize 的区别

1)final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。

2)finally:异常处理语句结构的一部分,表示总是执行。

3)finalize:Object 类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法 提供垃圾收集时的其他资源回收,例如关闭文件等。该方法更像是一个对象生命周期的临终方法,当该方法 被系统调用则代表该对象即将“死亡”,但是需要注意的是,我们主动行为上去调用该方法并不会导致该对象“死亡”,这是一个被动的方法(其实就是回调方法),不需要我们调用。

 

13、基本数据类 型

13.1  基本数据类型

数字类型 :byte、short、int、long、float、double

字符类型:char

布尔型:boolean

这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、 Character、Boolean

13.2  为什么需要包装类

为了能够将这些基本 数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型。

因为 Java 是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将 int 、double 等类型放进去的。因为集合的容器要求元素是 Object 类型。

为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。

13.3  自动装箱与拆箱

从 Java 5 开始引入了自动装箱/拆箱机制,使得基本数据类型和包装类可以相互转换。

装箱:将基本类型用它们对应的引用类型包装起来;

拆箱:将包装类型转换为基本数据类型;

如果整型字面量的值在-128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池 中的 Integer 对象,所以上面的面试题中 f1==f2 的结果是 true,而 f3==f4 的结果是 false。

 

posted @ 2023-01-30 09:48  百亩  阅读(91)  评论(0编辑  收藏  举报