[金三银四面试季]Java面向对象高频面试题

网友投稿 597 2022-05-30

金三银四正是跳槽涨薪的好时节,波哥给大家整理了很多面试高频的问题,助力大家找到更好的工作哦,点点关注,感谢支持。

面向对象面试题

1.谈谈你对面向对象思想的理解?

面向过程编程(POP):是一种以过程为中心的编程思想

面向对象编程(OOP):相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式

2. 谈谈你对面向对象的特征的理解

封装

即把对象的属性和方法包裹起来, 只能通过约定好的接口实现访问

封装是一种信息隐藏技术,在java中通过关键字private实现封装。

package com.bobo.interview0001.domain; /** * 属性+方法 */ public class User { // 将age设置为私有属性 private int age; public String name; /** * 提供给外界设置age属性的方法 * @param age */ public void setAge(int age){ if(age < 0 || age > 120){ System.out.println("age的设置不合法!!!"); age = 0; } this.age = age; } /** * 提供给外界获取age属性的方法 * @return */ public int getAge(){ return age; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

封装的好处:可以将私有数据和公共数据分离,保护私有数据,提高了程序的安全性!!!

继承

继承是面向对象的最显著的一个特征。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

为什么要继承呢:

反映现实的真实关系

减少代码冗余

对父类的属性和方法进行扩展和重写

继承中,子类不可以选择性的继承父类的东西,而是全部继承父类的属性和方法。其中父类又叫超类或基类,子类又叫派生类。父类是子类的一般化,子类是父类的特化(具体化)。java中不支持多继承,一个类最多只能有一个父类。而在java中多继承是通过接口实现的。

public class Person { private String userName; private int age; private String sex; private String address; private String idCard; public void show(){ System.out.println("父类中的方法"); } }

1

2

3

4

5

6

7

8

9

10

11

12

public class Doctor extends Person{ @Override public void show() { System.out.println("Doctor ... show"); } }

1

2

3

4

5

6

public class Student extends Person{ private String ClassNum; private String stuNo; // .... public void learn(){ } @Override public void show() { System.out.println("Student ... show"); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

多态

Java对象的多种状态,多态主要是基于继承和重写,最终可以实现相同的类型调用相同的方法,结果不相同

public static void main(String[] args) { // 多态的体现 Person p1 = new Student(); p1.show(); Person p2 = new Doctor(); p2.show(); }

1

2

3

4

5

6

7

3.介绍下访问权限修饰符

重点注意protected和default,protected相比较default多了一个不同包下面的子类可见

4. String是基本数据类型吗?

Java中的8个基本数据类型:

byte

short

int

long

float

double

char

boolean

String类型显然不在8个基本数据类型之中,String类型是我们讲的引用类型

5. float f=6.6这样写是否正确?

不正确的,6.6默认是双精度类型,将double类型赋值给float类型,属性向下转型,会造成精度丢失

public class Demo01 { public static void main(String[] args) { // 小数 默认的是 double类型 双精度类型 向下转型 精度丢失 float f2 = 1.1; float f = 6.6F; float f1 = 7.7F; } }

1

2

3

4

5

6

7

8

9

6 程序题1

short s1 = 1; s1 = s1 + 1; // 此处需要强制类型转换

1

2

有错吗? – 有错

short s1 = 1; s1 += 1; // s1 = (short) ( s1 + 1 )

1

2

有错吗?-- 没有错

7. 程序题2

以下程序的输出结果是:

public static void main(String[] args) { Integer f1=100,f2=100,f3=150,f4=150; System.out.println(f1==f2); System.out.println(f3==f4); }

1

2

3

4

5

输出结果:true,false

原因解释:

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; // 创建了一个缓存的数组 长度256 cache = new Integer[(high - low) + 1]; int j = low; // 循环256次 for(int k = 0; k < cache.length; k++) // 一次创建 -128 到 127 对应的Integer对象,并保存到数组中 cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

valueOf(int)中的源码

public static Integer valueOf(int i) { // 如果 i 在 -128 到 127之间 if (i >= IntegerCache.low && i <= IntegerCache.high) // 就从缓存的数组中返回之前创建的 Integer对象 return IntegerCache.cache[i + (-IntegerCache.low)]; // 否则创建一个新的Integer对象 return new Integer(i); }

1

2

3

4

5

6

7

8

提醒:越是貌似简单的面试题,其中的玄机就越多,要求面试者需要相对深厚的功力。

8. &和&&的区别

&:按位与

int a = 5; // 101 int b = 3; // 011 int c = a & b;// 001

1

2

3

逻辑与:都为true结果才为true,左边如果为false,右边表达式还是会运行

&&:短路与:都为true结果才为true,左边如果为false,右边表达式不会运行

public static void main(String[] args) { int a = 5; // 101 int b = 3; // 011 int c = a & b;// 001 System.out.println("c = " + c); // 逻辑与 & 左右两边都为true 那么结果才为true // 左边为false,结果为false,但是右边的表达式还是会进行运算 if(a == 0 & a ++ > 0){ System.out.println("------"); } System.out.println("a = " + a); // && 短路与 左边如果为false,那么整体的结果也为false,而且右边的表达式不会运算 if(a == 0 && a ++ > 0){ System.out.println("------"); } System.out.println("a = " + a); String name = null; // 短路与在实际使用的时候还是很有使用场景的 if(name != null & !name.equals("")){ } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

9. switch可以使用的数据类型

JDK5之前:byte,short,int,char

JDK5之后:Java中引入了枚举类型,枚举类型也可以

JDK7之后:可以使用String,但是long类型一直是不可以的

10. 用最有效的方法计算2乘以8?

2 << 3(左移3位,相当于乘以2的3次方)

11. 构造器是否可以被重写?

构造器不能被继承,因此是不能被重写的,但是我们可以重载构造器

12. 是否可以继承String类?

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

13. 重载和重写的区别

方法的重载和重写都是实现多态的方式, 区别在于前者实现的是编译时的多态性, 而后者实现的是运行时的多态性。

重载:在同一个类中,同名的方法如果有不同的参数列表(参数类型,参数个数,顺序)视为重载

package com.bobo.interview0006; public class Demo01 { public static void main(String[] args) { A a = new A(); a.show1(); a.show1(18); a.show1("a",18); a.show1(18,"aaa"); } } class A{ /** * 方法的重载:在同一个类中 方法名称相同 参数列表不同 */ public void show1(){ } public void show1(String name){ } public void show1(int a){ } public void show1(String name,int a){ } public void show1(int a,String name){ } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

重写:发生在父子关系类中,子类重写父类的方法,有相同的返回类型或子类

class B extends A{ /** * 重写,子类重写父类中的方法 */ @Override public void show1() { // super.show1(); System.out.println("show1 --- B"); } /** * 重写的返回类型必须相同或者是父类返回类型的子类 * @param n * @return */ @Override public Integer fun1(Number n) { return 666; } }

1

2

3

4

5

[金三银四面试季]Java面向对象高频面试题

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

14. 抽象类和接口的区别

接口和抽象类都不能实例化

抽象类相比于普通类多的限制是可以添加抽象方法

接口中只能声明常量和抽象方法且访问修饰符必须都为public,在JDK8之后可以定义default方法和static方法

package com.bobo.interview0007; public class Demo01 { public static void main(String[] args) { } } /** * 抽象类 * 不能被实例化 * 可以声明普通的属性和方法 * 还可以定义抽象方法 */ abstract class A{ private int a; public int b; public void fun1(){ } public abstract void fun2(); } /** * 接口 * 不能被实例化 * 属性只能是常量 * 方法只能是抽象方法 * JDK1.8之后可以有 default和static方法 */ interface B{ public final int a = 9; // public abstract 方法默认的类型 可以不添加 public abstract void show1(); /** * default方法 */ public default void fun1(){ } /** * 静态方法 */ public static void fun2(){ } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

15. 静态内部类和内部类的区别

静态内部类是被声明为static的内部类,它可以不依赖外部类的实例被实例化,而通常我们的内部类是需要通过外部类的实例才能实例化。

public class OutterClass { /** * 普通内部类 */ class InnerClass{ public void show(){ System.out.println("InnerClass ..."); } } /** * 静态内部类 */ static class StaticInnerClass{ public void show(){ System.out.println("StaticInnerClass ..."); } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

测试代码

public static void main(String[] args) { // 获取普通内部类对象 OutterClass.InnerClass innerClass = new OutterClass().new InnerClass(); innerClass.show(); // 获取静态内部类对象 OutterClass.StaticInnerClass staticInnerClass = new OutterClass.StaticInnerClass(); staticInnerClass.show(); }

1

2

3

4

5

6

7

8

16. 抽象方法是否可以同时是静态的本地方法及被synchronized修饰?

抽象方法所在的类肯定是抽象类,

不能被static修饰,因为静态不能被重写,而抽象方法必须要被子类重写,所以矛盾。

不能被native修饰,本地方法是由C代码实现的,而抽象方法没有实现,也是矛盾的

不能被synchronized修饰,同步是和方法的实现的细节有关系的,而抽象方法没有具体的实现,也是相互矛盾的

17. 静态变量和实例变量的区别

静态变量:是被static所修饰的变量,也称为类变量,它是属性类对象的,不属于类的任何一个对象。静态变量是对象间共享的

实例变量:数据具体的某个对象,必须要先创建对象,然后通过该对象来访问实例变量,

18.静态方法中是否可以访问普通方法

肯定不可以,静态方法是在类加载阶段就创建的,而普通方法是属性对象的,在类加载阶段还没有对象,所以是矛盾的,静态方法只能访问静态成员,普通方法必须要先创建对象,然后通过对象来调用普通方法。

public class Person { static void show(){ // 静态方法中不可以调用 普通方法 // speak(); } void speak(){ // 普通方法可以调用静态方法 show(); } }

1

2

3

4

5

6

7

8

9

10

11

12

19.如何实现克隆

package com.dpb.prototype; import java.io.Serializable; import java.util.Date; /** * 原型类:被克隆的类型 * @author dengp * */ public class User implements Cloneable,Serializable{ private String name; private Date birth; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 实现克隆的方法 */ public Object clone() throws CloneNotSupportedException{ return super.clone(); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

深度克隆

package com.dpb.prototype; import java.io.Serializable; import java.util.Date; /** * 原型类:被克隆的类型 * 深度克隆测试 * @author dengp * */ public class User2 implements Cloneable,Serializable{ private String name; private Date birth; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 实现克隆的方法 * 深度克隆(deep clone) */ public Object clone() throws CloneNotSupportedException{ Object object = super.clone(); // 实现深度克隆(deep clone) User2 user = (User2)object; user.birth = (Date) this.birth.clone(); return object; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

序列化和反序列化

public static void main(String[] args) throws CloneNotSupportedException, Exception { Date date = new Date(1231231231231l); User user = new User(); user.setName("波波烤鸭"); user.setAge(18); user.setBirth(date); System.out.println("-----原型对象的属性------"); System.out.println(user); System.out.println(user.getName()); System.out.println(user.getBirth()); //使用序列化和反序列化实现深复制 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(user); byte[] bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); //克隆好的对象! User user1 = (User) ois.readObject(); // 修改原型对象的值 date.setTime(221321321321321l); System.out.println(user.getBirth()); System.out.println("------克隆对象的属性-------"); System.out.println(user1); System.out.println(user1.getName()); System.out.println(user1.getBirth()); }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

20.介绍下final关键字

21.程序运行题

以下程序的输出结果是:

public class Hello { /** * Class 对象 * Object 普通对象 * Java的类加载机制要比较清楚 * @param args */ public static void main(String[] args) { A ab = new B(); // 普通对象是根据 Class对象来创建的 1a2b ab = new B(); // 因为 B的类对象已经被加载了,这时静态变量初始化就不会执行了 2b // 1a2b2b } } class A { static { System.out.print("1"); } public A() { System.out.print("2"); } } class B extends A{ static { System.out.print("a"); } public B() { super(); System.out.print("b"); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

输出结果为:

1a2b2b

1

程序执行的顺序:

创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。

22.数据类型之间的转换

字符串–》基本数据类型

基本数据类型–》字符串

public static void main(String[] args) { String s1 = "99"; // 转换为int类型 // 1.通过int对应的包装类 Integer 中提供的 parseXXX(String) 方法 int i = Integer.parseInt(s1); // 2.通过int 对应的包装来 Integer 中的 valueOf(String) 方法 Integer i2 = Integer.valueOf(s1); // 将一个基本数据类型转换为 String类型 int age = 18; String s2 = age + ""; String s3 = String.valueOf(age); }

1

2

3

4

5

6

7

8

9

10

11

12

13

23.return和finally的执行顺序问题

分析如下的代码程序,给出输出结果,并给出原因

public class Demo02 { public static void main(String[] args) { Demo02 demo02 = new Demo02(); System.out.println(demo02.getName("bobo")); } public String getName(String name){ String res = ""; try { res = name; return res; }finally { res = "波波烤鸭"; } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

输出的结果是:bobo

原因:通过指令分析我们可以发现在 return 代码执行的时候会将局部变量保存在 栈帧的顶部,然后在finally中修改的还是原来的栈帧位置的局部变量,最终返回的信息还是栈帧顶部的变量,所以finally代码块在return关键字之后会执行,但是不会改变栈帧顶部的信息。

还有一种情况需要注意,如果finally和try块中都有return关键字会怎么样呢?

public class Demo02 { public static void main(String[] args) { Demo02 demo02 = new Demo02(); System.out.println(demo02.getName("bobo")); } public String getName(String name){ String res = ""; try { res = name; return res; }finally { res = "波波烤鸭"; return res; // 指令中返回的就不是栈帧顶部的数据了 而是 res 对应的栈帧位置 } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

通过指令我们可以看到在finally中的return关键字的指令返回的就是finally中的局部变量的信息,可以理解为finally中的return会覆盖掉try块中的return逻辑。

Java 面向对象编程

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Excel实用操作技巧有哪些(excel中比较实用的技巧)
下一篇:【前端领域高频笔试面试】—— JavaScript高级相关(前端高频面试题)
相关文章