面向对象
- 15. 面向对象和面向过程的区别?
- 16. 面向对象的基本特征
- 17.重载(overload)和重写(override)的区别?
- 18.访问修饰符public、private、protected、以及不写(默认)时的区别?
- 19.this关键字有什么作用?
- 20.抽象类(abstract class)和接口(interface)有什么区别?
- 21.成员变量与局部变量的区别有哪些?
- 22.静态变量和实例变量的区别?静态方法、实例方法呢?
- 23.final关键字有什么作用?
- 24. final, finally, finalize 的区别?
- 26.==和 equals 的区别?
- 27.hashCode与 equals?
- 28.Java是值传递,还是引用传递?
- 29.深拷贝和浅拷贝?
- 30.Java 创建对象有哪几种方式?
接前文
15. 面向对象和面向过程的区别?
- 面向过程:面相过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候在一个一个的一次调用就可以了。
- 面向对象:面向对象,把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事件在解决整个问题的过程中所发生的行为。目的是为了写出通用的代码,加强代码的重用,屏蔽差异性。
16. 面向对象的基本特征
- 封装:
- 封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法。
- 继承
- 继承是使用已经存在的类的定义作为基础床加你新的类,新类的定义可以增加新的属性或新的方法,也可以继承父类的属性和方法。通过继承可以很方便地进行代码复用
- 关于继承有以下三个要点:
- 子类拥有父类对象的所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- 多态
- 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用方法发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。
- 在 Java 中有两种形式可以实现多态:继承(多个子类对同一个方法的重写)和接口(实现接口并覆盖接口中同一方法)
17.重载(overload)和重写(override)的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
- 重载发生在一个类中,同名的方法如果有同的参数列表(参数类型不同,参数个数不同或者二者都不同)则视为重载。
- 重写发生在子类与父类之间,重写要求子类被重写方法与父类重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类重写方法声明更多的异常(里氏代换原则)。
方法重载的规则:
- 方法名一致,参数列表中参数的顺序,类型,个数不同。
- 重载与方法的返回值无关,存在与父类和子类,同类中。
- 可以抛出不同的异常,可以有不同修饰符。
18.访问修饰符public、private、protected、以及不写(默认)时的区别?
Java 中,可以使用访问控制符来保护类、变量、方法和构造方法的访问。Java 支持4 中不同的访问权限。
- default(默认,什么也不写):在同一个包内可见,不使用任何修饰符。可以修饰在类,接口,变量,方法
- private:在同一个类内可见,可以修饰变量、方法。注意:不能修饰类(外部类)
- public:对所有类可见。可以修饰类、接口、变量、方法。
- protected:对同一个包内的类和所有子类可见。可以修饰变量、方法。注意不能修饰类(外部类)
19.this关键字有什么作用?
this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。
- 普通的直接引用,this相当于是指向当前对象本身
- 形参与成员变量名字重名,用this来区分:
- 引用本类的构造函数
20.抽象类(abstract class)和接口(interface)有什么区别?
- 方法实现: 接口中的方法默认是公开的(public), 并且不能有任何实现. 从 Java8 开始, 接口中的方法可以有默认实现, 而抽象类可以包含非抽象的方法.
- 变量: 接口中只能包含静态(static)和常量(final)变量, 不能有其他类型的变量. 而抽象类中可以包含任意类型的变量.
- 实现: 一个类可以实现多个接口, 但只能继承一个抽象类. 接口自身可以通过 extends 关键字扩展多个接口
- 方法修饰符: 接口中的方法默认修饰符是 public, 抽象方法可以有 public, protected 和 default 修饰符. 抽象方法是为了被重写, 所以不能使用private 修饰符
- 设计层面: 抽象类是对类的抽象, 是一种模版设计, 而接口是对行为的抽象, 是一种 行为规范
21.成员变量与局部变量的区别有哪些?
- 从语法形式上看: 成员变量是属于类的, 而局部变量实在方法总定义的变量或是方法的参数; 成员变量可以被 public, private, static等修饰符修饰,而局部变量不能被访问控制修饰符以及static所修饰; 但是, 成员变量和局部变量都能被 final 所修饰
- 从变量在内存中的存储方式来看: 如果成员量是使用 static 修饰的, 那么这个成员量是属于类的, 如果没有使用 static 修饰, 这个成员变量是属于实例的. 对象存于 堆内存, 如果局部变量类型为基本数据类型, 那么存储在 栈内存, 如果为引用数据类型, 那么存放的是指向堆内存对象的引用或是指向常量池中的地址
- 从变量在内存中的生存时间上看: 成员变量是对象的一部分, 它随着对象的创建而存在, 而局部变量的随着方法的调用而自动消失
- 成员变量如果没有被赋予初值: 则会自动以类型的默认值而赋值(一种例外: 被final 修饰的成员变量也必须显示地赋值), 而局部变量则不会自动赋值.
22.静态变量和实例变量的区别?静态方法、实例方法呢?
静态变量和实例变量的区别?
静态变量: 是被 static 修饰符修饰的变量, 也称为类变量, 它属于类,不属于类的任何一个对象, 一个类不管创建多少个对象, 静态变量在内存中有且仅有一个副本.
实例变量: 必须依存于某一个实例, 而需要先创建对象然后通过对象才能访问它, 静态变量可以实现让多个对选哪个共享内存.
静态方法和实例方法有何不同?
静态方法: static 修饰的方法, 也称为类方法, 在外部调用静态方法时, 可以使用 “类名.方法名” 的方式, 也可以使用 “对象名.方法名” 的方式, 静态方法里不能访问类的非静态成员变量和方法
实例方法: 依存于类的实例, 相遇奥使用 “对象名.方法名” 的方式调用; 可以访问类的所有成员方法和变量
23.final关键字有什么作用?
final 表示不可变的意思, 可用于修饰类、属性和方法;
- 被 final 修饰的类不可以被继承
- 被 final 修饰的方法不可以被重写
- 被 final 修饰的变量不可变,被final 修饰的变量必须被显式的指定初始值(就是该变量只能赋值一次),还得注意的是,这里的不可变指的是变量的引用不可变,不是引用指向的内容的不可变
24. final, finally, finalize 的区别?
- final 用于修饰变量, 方法和类: final 修饰的类不可被继承; 修饰的方法不可被重写; 修饰的变量不可变.
- finally 作为异常处理的一部分, 他只能在 try/catch 语句中, 并且附带一个语句块表示这段语句最终一定被执行 (无论是否抛出异常), 经常被用在需要释放资源的情况下, System.exit(0) 可以阻断 finally 执行
- finalize 是在 java.lang.Object 里定义的方法, 也就是说每一个对象都有这么一个方法, 这个方法在 gc 启动, 该对象被回收的时候被调用
一个对象的 finalize 方法只会被调用一次, finalize 被调用不一定会立即回收该对象, 所以有可能调用finalize 后, 该对象又不需要被回收了, 然后到了真正要被回收的时候, 因为前面调用过一次, 所以不会再次调用 finalize了, 进而产生问题, 因此不推荐使用 finalize 方法
26.==和 equals 的区别?
== : 它的作⽤是判断两个对象的地址是不是相等。即,判断两个对象是不是同⼀个对象(基本数据类型==比较的是值,引⽤数据类型==比较的是内存地址)。
equals() : 它的作⽤也是判断两个对象是否相等。但是这个“相等”一般也分两种情况:
- 默认情况:类没有覆盖 equals() ⽅法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象,还是相当于比较内存地址。
- 自定义情况:类覆盖了 equals() ⽅法。我们平时覆盖的 equals()方法一般是比较两个对象的内容是否相同,自定义了一个相等的标准,也就是两个对象的值是否相等。
举个例⼦,Person,我们认为两个人的编号和姓名相同,就是一个人:
27.hashCode与 equals?
什么是 hashCode?
hashCode方法用于获取对象的哈希码,哈希码是一个整数,用来表示对象的唯一标识。在Java中,哈希码常用于哈希表等数据结构的实现中,可以快速定位对象。
哈希码主要在哈希表这类集合映射的时候用到,哈希表存储的是键值对(key-value),它的特点是:能根据“键”快速的映射到对应的“值”。这其中就利⽤到了哈希码!
为什么要有 hashCode?
主要是在哈希表这种结构中用的到。
例如HashMap怎么把key映射到对应的value上呢?用的就是哈希取余法,也就是拿哈希码和存储元素的数组的长度取余,获取key对应的value所在的下标位置。
为什么重写 equals 时必须重写 hashCode ⽅法?
如果两个对象相等, 则 hashcode 一定也是相同的. 两个对象相等, 对两个对象分别调用 equals 方法都返回 true. 反之, 两个对象又相同的 hashcode 值, 它们也不一定是相等. 因此, equals 方法被覆盖过, 则 hashcode 方法也必须被覆盖.
hashcode 的默认行为是对 堆上的对象产生独特值, 如果没有重写 hashcode, 则该 class 的两个对象无论如何都不会相等 (即使这两个对象指向相同的数据)
为什么两个对象有相同的 hashcode 值, 它们也不一定是相等的?
因为可能会碰撞, hashcode() 所使用的散列算法也许刚好会让多个对象传回相同的散列值. 越糟糕的散列算法越容易碰撞, 但这也与数据值域分布的特性有关 (所谓碰撞也就是指的是不同的对象得到相同的 hashcode)
28.Java是值传递,还是引用传递?
Java 语言是值传递, Java 语言的方法调用只支持参数的值传递. 当一个对象实例作为一个参数被传递到方法中时, 参数的值就是对该对象的调用. 对象的属性可以在被调用过程中被改变, 但对对象的引用的改变是不会影响调用者的.
JVM 的内存分为堆和栈, 其中栈中存储了基本数据类型和引用数据类型实例的地址, 也就是对象地址.
而对象所占的空间是在堆中开辟的, 所以传递的时候可以理解为把变量存储的对象地址传递过去, 因此引用类型也是值传递.
29.深拷贝和浅拷贝?
- 浅拷贝: 仅拷贝被拷贝对象的成员变量值, 也就是基本数据类型变量的值, 和引用数据类型变脸的地址值, 而对于引用类型变量指向的堆中的对象不会被拷贝
- 深拷贝: 完全拷贝一个对象, 拷贝被拷贝对象的成员变量的值, 堆中的对象也会拷贝一份
因此深拷贝是安全的,浅拷贝的话如果有引用类型,那么拷贝后对象,引用类型变量修改,会影响原对象。
浅拷贝如何实现呢?
- Object类提供的clone()方法可以非常简单地实现对象的浅拷贝。
深拷贝如何实现呢?
- 重写克隆方法:重写克隆方法,引用类型变量单独克隆,这里可能会涉及多层递归。
- 序列化:可以先将原对象序列化,再反序列化成拷贝对象。
30.Java 创建对象有哪几种方式?
Java中有以下四种创建对象的方式:
- new创建新对象
- 通过反射机制
- 采用clone机制
- 通过序列化机制
前两者都需要显式地调用构造方法。对于clone机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在Java中序列化可以通过实现Externalizable或者Serializable来实现。