面向对象相关-这一篇全了解

1、什么是面向对象,什么是面向过程。面向对象的三大基本特征和五大基本原则是什么?

解:

什么是面向过程?

把问题分解成一个一个步骤,每个步骤用函数实现,依次调用即可。就是说,在进行面向过程编程的时候,不需要考虑那么多,上来先定义一个函数,然后使用各种诸如if-else、for-each等方式进行代码执行。最典型的用法就是实现一个简单的算法,比如实现冒泡排序。(自己在家做饭)

什么是面向对象?

将问题分解成一个一个步骤,对每个步骤进行相应的抽象,形成对象,通过不同对象之间的调用,组合解决问题。就是说,在进行面向对象进行编程的时候,要把属性、行为等封装成对象,然后基于这些对象及对象的能力进行业务逻辑的实现。比如想要造一辆车,上来要先把车的各种属性定义出来,然后抽象成一个Car类。(点外卖)

面向对象的三大基本特征和五大基本原则?

三大基本特征:封装、继承(上次外卖点过的再来一单)、多态(可以对上次点的在购物车中在增删修改)。

五大基本原则:单一职责原则(Single-Responsibility Principle)、开放封闭原则(Open-Closed principle)、Liskov替换原则(Liskov-Substituion Principle)、依赖倒置原则(Dependency-Inversion Principle)和 接口隔离原则(Interface-Segregation Principle)。关于五大基本原则的具体内容参见我的博文:链接:牢记面向对象五个基本原则-HollisChuang's Blog

后话:

这些东西虽然都是概念性的,但是很多程序员根本不知道,更别提如何更好的面向对象编程了,我就看过有些同学写的代码,从头到尾一个函数,各种if-else,完全不考虑复用、扩展等。这种代码非常不利于阅读和维护。希望球友们不仅记住上面的概念,还要通汇贯通,切实的运用到平时的工作中。

2、Java和C++同为面向对象语言,Java和C++主要区别有哪些?双方个有哪些优缺点?

解:

C++ 被设计成主要用在系统性应用程序设计上的语言,对C语言进行了扩展。对于C语言这个为运行效率设计的过程式程序设计语言, C++ 特别加上了以下这些特性的支持:静态类型的面向对象程序设计的支持、异常处理、RAII以及泛型。另外它还加上了一个包含泛型容器和算法的C++库函数。

Java 最开始是被设计用来支持网络计算。它依赖一个虚拟机来保证安全和可移植性。Java包含一个可扩展的库用以提供一个完整的的下层平台的抽象。Java是一种静态面向对象语言,它使用的语法类似C++,但与之不兼容。为了使更多的人到使用更易用的语言,它进行了全新的设计。

其实,最主要的区别是他们分别代表了两种类型的语言:

C++是编译型语言(首先将源代码编译生成机器语言,再由机器运行机器码),执行速度快、效率高;依赖编译器、跨平台性差些。

Java是解释型语言(源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。),执行速度慢、效率低;依赖解释器、跨平台性好。

PS:也有人说Java是半编译、半解释型语言。Java 编译器(javac)先将java源程序编译成Java字节码(.class),JVM负责解释执行字节码文件。

二者更多的主要区别如下:

C++是平台相关的,Java是平台无关的。

C++对所有的数字类型有标准的范围限制,但字节长度是跟具体实现相关的,不同操作系统可能。Java在所有平台上对所有的基本类型都有标准的范围限制和字节长度。

C++除了一些比较少见的情况之外和C语言兼容 。 Java没有对任何之前的语言向前兼容。但在语法上受 C/C++ 的影响很大

C++允许直接调用本地的系统库 。 Java要通过JNI调用, 或者 JNAC++允许过程式程序设计和面向对象程序设计 。Java必须使用面向对象的程序设计方式C++支持指针,引用,传值调用 。Java只有值传递。

C++需要显式的内存管理,但有第三方的框架可以提供垃圾搜集的支持。支持析构函数。

Java 是自动垃圾收集的。没有析构函数的概念。C++支持多重继承,包括虚拟继承 。

Java只允许单继承,需要多继承的情况要使用接口。

3、什么是平台无关性,Java是如何做到平台无关的?

解:

跨平台指的是一种语言在计算机上的运行不受平台的约束,一次编译,到处执行。 平台无关有两种:源代码级和目标代码级。

我们常说的跨平台,或者平台无关,指的就是目标代码,或者说是软件交付件跨平台。

C和C++具有一定程度的源代码级平台无关,表明用C或C++写的应用程序不用修改只需重新编译就可以在不同平台上运行。但是,关键是要重新编译。可是,一般软件交付都是给你个成品,对于C或者C++开发出的软件,只能运行在某个平台的。没有源码,怎么编译。

Java编译出来的是字节码,去到哪个平台都能用,只要有那个平台的JDK就可以运行,所以,Java程序的最大优势就是平台无关。对于Java,交付的就是一堆jar包或者war包,只要系统上有个Java虚拟机,就可以直接运行,这不就是跨平台了么。

我们使用的C、C++还有Java等高级语言,都需要最终被编译成机器语言,才能被计算机执行。

C语言和C++语言的编译过程是把源代码编译生成机器语言。这样机器可以直接执行。但是不同系统对同一段“机器语言”的处理结果可能是不一样的,原因可能有很多,比如CPU的指令集不同的。C语言不能实现跨平台运行,就是因为它编译出来的文件的格式,只适用于某种cpu,其他cpu无法识别。

那么Java是如何实现跨平台的呢?

我们编写的Java源码,编译后会生成一种 .class 文件,称为字节码文件。这种字节码文件需要经过JVM虚拟机,然后翻译成机器语言,才能被机器执行。

那么,由于我们可以在不同的操作系统上安装不同的JVM,而JVM封装了所有对于.class文件的处理,即JVM帮我们把字节码翻译成机器语言的过程中就已经充分考虑到对应平台的特性了。比如,一个.class文件,在不同机器上最终生成的机器语言可能是不同的,但是这种不同不需要我们关心,JVM会保证他可以正常运行,完整的表达正确的程序语义。

4、什么是值传递,什么是引用传递。为什么说Java中只有值传递

https://blog.csdn.net/w372426096/article/details/82216742

5、 什么是编译,什么是反编译。Java的编译和反编译方法。

解:

在介绍编译和反编译之前,我们先来简单介绍下编程语言(Programming Language)。编程语言(Programming Language)分为低级语言(Low-level Language)和高级语言(High-level Language)。

机器语言(Machine Language)和汇编语言(Assembly Language)属于低级语言,直接用计算机指令编写程序。

而C、C++、Java、Python等属于高级语言,用语句(Statement)编写程序,语句是计算机指令的抽象表示。

上面提到语言有两种,一种低级语言,一种高级语言。可以这样简单的理解:低级语言是计算机认识的语言、高级语言是程序员认识的语言。

那么如何从高级语言转换成低级语言呢?这个过程其实就是编译。将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译为计算机能解读、运行的低阶机器语言的程序的过程就是编译。负责这一过程的处理的工具叫做编译器。

反编译的过程与编译刚好相反,就是将已编译好的编程语言还原到未编译的状态,也就是找出程序语言的源代码。就是将机器看得懂的语言转换成程序员可以看得懂的语言。Java语言中的反编译一般指将class文件转换成java文件。

Java语言中负责编译的编译器是一个命令:javac

javac是收录于JDK中的Java语言编译器。该工具可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。

当我们写完一个HelloWorld.java文件后,我们可以使用javac HelloWorld.java命令来生成HelloWorld.class文件,这个class类型的文件是JVM可以识别的文件。通常我们认为这个过程叫做Java语言的编译。其实,class文件仍然不是机器能够识别的语言,因为机器只能识别机器语言,还需要JVM再将这种class文件类型字节码转换成机器可以识别的机器语言。

Java中反编译工具有多个,如javap ,jad,CRF等。

详见 链接:Java开发必会的反编译知识(附支持对Lambda进行反编译的工具)

6、语法糖

https://blog.csdn.net/w372426096/article/details/80507783

7、Java 8中的lambda表达式是不是语法糖,具体如何实现的。

解:

关于lambda表达式,网上有人说他并不是语法糖,也有人说他是匿名内部类的语法糖。其实我想纠正下这个说法。Labmda表达式不是匿名内部类的语法糖,但是他也是一个语法糖。实现方式其实是依赖了几个JVM底层提供的lambda相关api。

先来看一个简单的lambda表达式。遍历一个list:

public static void main(String... args) {

     List<String> strList = ImmutableList.of("Hollis", "公众号:Hollis", "博客:www.hollischuang.com");

     strList.forEach( s -> { System.out.println(s); } );

}

为啥说他并不是内部类的语法糖呢,#直面Java#的第006期介绍过的,内部类在编译之后会有两个class文件,但是,包含lambda表达式的类编译后只有一个文件。

反编译后代码如下:

public static /* varargs */ void main(String ... args) {

        ImmutableList strList = ImmutableList.of((Object)"Hollis", (Object)"\u516c\u4f17\u53f7\uff1aHollis", (Object)"\u535a\u5ba2\uff1awww.hollischuang.com");

 strList.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());

}

private static /* synthetic */ void lambda$main$0(String s) {

       System.out.println(s);

}

可以看到,在forEach方法中,其实是调用了java.lang.invoke.LambdaMetafactory#metafactory方法,该方法的第四个参数implMethod指定了方法实现。可以看到这里其实是调用了一个lambda$main$0方法进行了输出。

再来看一个稍微复杂一点的,先对List进行过滤,然后再输出:

public static void main(String... args) {

List<String> strList = ImmutableList.of("Hollis", "公众号:Hollis", "博客:www.hollischuang.com");

List HollisList = strList.stream().filter(string -> string.contains("Hollis")).collect(Collectors.toList());

HollisList.forEach( s -> { System.out.println(s); } ); }

//反编译后代码如下:

public static /* varargs */ void main(String ... args) {

ImmutableList strList = ImmutableList.of((Object)"Hollis", (Object)"\u516c\u4f17\u53f7\uff1aHollis", (Object)"\u535a\u5ba2\uff1awww.hollischuang.com");

List<Object> HollisList = strList.stream().filter((Predicate<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)Z, lambda$main$0(java.lang.String ), (Ljava/lang/String;)Z)()).collect(Collectors.toList());

HollisList.forEach((Consumer<Object>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$1(java.lang.Object ), (Ljava/lang/Object;)V)()); }

private static /* synthetic */ void lambda$main$1(Object s) {

System.out.println(s);

}

private static /* synthetic */ boolean lambda$main$0(String string) {

return string.contains("Hollis");

}

两个lambda表达式分别调用了`lambda$main$1`和`lambda$main$0`两个方法。 所以,lambda表达式的实现其实是依赖了一些底层的api,在编译阶段,编译器会把lambda表达式进行解糖,转换成调用内部api的方式。

8、什么是多态,多态有什么好处,多态的必要条件是什么、Java中多态的实现方式

解:

多态的概念比较简单,就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

如果按照这个概念来定义的话,那么多态应该是一种运行期的状态。

为了实现运行期的多态,或者说是动态绑定,需要满足三个条件。

即有类继承或者接口实现、子类要重写父类的方法、父类的引用指向子类的对象。

简单来一段代码解释下:

public class Parent{

public void call(){

sout("im Parent");

}}

public class Son extends Parent{// 1.有类继承或者接口实现

public void call(){// 2.子类要重写父类的方法sout("im Son");

}}

public class Daughter extends Parent{// 1.有类继承或者接口实现

public void call(){// 2.子类要重写父类的方法sout("im Daughter");

}}

public class Test{

public static void main(String[] args){

Parent p = new Son(); //3.父类的引用指向子类的对象

Parent p1 = new Daughter(); //3.父类的引用指向子类的对象

}}

这样,就实现了多态,同样是Parent类的实例,p.call 调用的是Son类的实现、p1.call调用的是Daughter的实现。

有人说,你自己定义的时候不就已经知道p是son,p1是Daughter了么。但是,有些时候你用到的对象并不都是自己声明的啊 。

比如Spring 中的IOC出来的对象,你在使用的时候就不知道他是谁,或者说你可以不用关心他是谁。根据具体情况而定。

另外,还有一种说法,包括维基百科也说明,动态还分为动态多态和静态多态。

上面提到的那种动态绑定认为是动态多态,因为只有在运行期才能知道真正调用的是哪个类的方法。

还有一种静态多态,一般认为Java中的函数重载是一种静态多态,因为他需要在编译期决定具体调用哪个方法、

关于这个动态静态的说法,我更偏向于重载和多态其实是无关的。

但是也要看情况,普通场合,我会认为只有方法的重写算是多态,毕竟这是我的观点。但是如果在面试的时候,我“可能”会认为重载也算是多态,毕竟面试官也有他的观点。我会和面试官说:我认为,多态应该是一种运行期特性,Java中的重写是多态的体现。不过也有人提出重载是一种静态多态的想法,这个问题在StackOverflow等网站上有很多人讨论,但是并没有什么定论。我更加倾向于重载不是多态。

这样沟通,既能体现出你了解的多,又能表现出你有自己的思维,不是那种别人说什么就是什么的。

9、什么是方法重写,什么是方法重载,成员变量可以被重写吗?

解:

重载(Overloading)和重写(Overriding)是Java中两个比较重要的概念。但是对于新手来说也比较容易混淆。

重载

简单说,就是函数或者方法有同样的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

重写

重写指的是在Java的子类与父类中有两个名称、参数列表都相同的方法的情况。由于他们具有相同的方法签名,所以子类中的新方法将覆盖父类中原有的方法。

关于重载和重写,你应该知道以下几点:

1、重载是一个编译期概念、重写是一个运行期间概念。

2、重载遵循所谓“编译期绑定”,即在编译时根据参数变量的类型判断应该调用哪个方法。

3、重写遵循所谓“运行期绑定”,即在运行的时候,根据引用变量所指向的实际对象的类型来调用方法

4、因为在编译期已经确定调用哪个方法,所以重载并不是多态。而重写是多态。重载只是一种语言特性,是一种语法规则,与多态无关,与面向对象也无关。(注:严格来说,重载是编译时多态,即静态多态。但是,Java中提到的多态,在不特别说明的情况下都指动态多态)

正是因为Java在继承中有方法的重写,所以,这也体现了Java的动态多态性。

重写,指的是方法。并没有提到成员变量。成员变量不会被重写,这里就有另外一个词:隐藏。

在一个类中,子类中的成员变量如果和父类中的成员变量同名,那么即使他们类型不一样,只要名字一样。父类中的成员变量都会被隐藏。在子类中,父类的成员变量不能被简单的用引用来访问。而是,必须从父类的引用获得父类被隐藏的成员变量,一般来说,我们不推荐隐藏成员变量,因为这样会使代码变得难以阅读。其实,简单来说,就是子类不会去重写覆盖父类的成员变量,所以成员变量的访问不能像方法一样使用多态去访问。

更多内容参考:链接:深入理解Java中的重写和重载-HollisChuang's Blog链接:Java中方法的重写与成员变量的隐藏-HollisChuang's Blog

10、接口和抽象类的区别,如何选择?

解:

接口和抽象类,最明显的区别就是接口只是定义了一些方法而已,在不考虑Java8中default方法情况下,接口中是没有实现的代码的。

抽象类中的抽象方法可以有public、protected和default这些修饰符,而接口中默认修饰符是public。不可以使用其它修饰符。

关于如何选择,我们一般会把接口暴露给外部,然后在业务代码中实现接口。如果多个实现类中有相同可复用的代码,则在接口和实现类中间加一层抽象类,将公用部分代码抽出到抽象类中。可以参考下模板方法模式,这是一个很好的理解接口、抽象类和实现类之间关系的设计模式。

11、Java能不能多继承,可不可以多实现?

解:

Java中不能多继承,可以多实现。 即定义一个类的时候,可以同时实现多个接口,但是不能同时继承多个类。

12、什么是构造函数,什么是默认构造函数?

解:

构造函数,是一种特殊的方法。 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。 特别的一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的不同来区分它们即构造函数的重载。

构造函数跟一般的实例方法十分相似;但是与其它方法不同,构造器没有返回类型,不会被继承,且可以有范围修饰符。构造器的函数名称必须和它所属的类的名称相同。 它承担着初始化对象数据成员的任务。

如果在编写一个可实例化的类时没有专门编写构造函数,多数编程语言会自动生成缺省构造器(默认构造函数)。默认构造函数一般会把成员变量的值初始化为默认值,如int -> 0,Integet -> null。

13、构造方法能不能被重载,构造方法能不能被重写?

解:

构造方法可以被重载,实现对象数据不同的初始化; 构造方法不能被重写,子类不能继承父类的构造方法,只能在子类的构造方法中调用父类的构造方法(自动调用父类默认构造方法,或调用父类指定的构造方法),保证父类对象也进行初始化(子类继承父类对象数据得到初始化)

14、对于成员变量和方法的作用域,public,protected,private以及不写之间的区别。

解:

-public :表明该成员变量或者方法是对所有类或者对象都是可见的,所有类或者对象都可以直接访问

- private:表明该成员变量或者方法是私有的,只有当前类对其具有访问权限,除此之外其他类或者对象都没有访问权限.子类也没有访问权限.

- protected:表明成员变量或者方法对类自身,与同在一个包中的其他类可见,其他包下的类不可访问,除非是他的子类

- default:表明该成员变量或者方法只有自己和其位于同一个包的内可见,其他包内的类不能访问,即便是它的子类

15、接口能否继承接口? 抽象类能否实现接口? 抽象类能否继承具体类?

解:

接口可以继承

抽象类可以实现接口

抽象类可以继承具体类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值