面向对象基础知识及面试题详解
面向对象(oop)1.初识面向对象
2. 方法回顾和加深
类和对象的关系
3. 对象的创建分析
创建与初始化对象
构造器详解
创建对象 内存分析
小结:
4. 面向对象三大特性
封装
继承
多态
抽象类
接口
6. 内部类
## 1.初识面向对象
- 面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么..
- 面向过程适合处理一些较为简单的问题
- 面向对象思维
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
- 对于描述复杂的事物,为了从宏观上把握,从整体上分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
- 面向对象编程(oop)
- 面向对象编程的本质就是:**以类的方式组织代码,以对象的方式组织(封装)数据。**
- 抽象
- 三大特征
- 封装
- 继承
- 多态
- 从认识的角度考虑是先有对象后有类。对象,是具体的事物。类是抽象的,是对对象的抽象
- 从代码运行角度考虑是先有类后有对象。类是对象的模板
## 2. 方法回顾和加深
- 方法的定义:
- 修饰符
- 返回类型
- break:跳出switch,结束循环 continue。结束方法 返回一个值return
- 方法名:注意规范就ok见名知意
- 参数列表:(参数类型,参数名)...
- 异常抛出:疑问,后面讲解
- 方法的调用
- 静态方法 static修饰
- 非静态方法
静态方法不可以直接调用同一个类中的非静态方法:因为静态方法是和类一块存在的,也就是有类的时候就有了静态方法。但是非静态方法是跟随类实例化的(对象)也就是有了new的类对象之后,才有非静态方法。
当一个已经存在的调用一个不存在的,所以就会报错。
同时:一个类中非静态可以直接调用非静态。静态可以直接调用静态。
- 形参和实参(类型要一致)
- 值传递和引用传递
```java
/**
* @Auther: 王海新
* @Date: 2021/1/31 15:29
* @Description: 值传递
*/
public class demo1 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
test(1);
System.out.println(a);
}
private static void test(int i) {
i = 10;
}
}
```
两个输出结果都为1。.因为是值传递,它只是把数值传递过去。原来的a并没与变。
引用传递:
```java
package com.wang.opp.demo1;
/**
* @Auther: 王海新
* @Date: 2021/1/31 15:59
* @Description: 引用传递 对象 : 本质还是值传递。我们之前说过,java只有值传递。
*/
public class demo2 {
public static void main(String[] args) {
Perosn perosn = new Perosn();
System.out.println(perosn.name);
test(perosn);
System.out.println(perosn.name);
}
private static void test(Perosn i) {
//i是一个对象。指向了上面的Perosn perosn = new Perosn();就像是一个具体的人,
// 可以改变它的属性,所以再次输出时,这个对象的name就改变了
i.name = "海新";
}
}
/**
* @Description: 定义一个类。在一个文件中可以有多个类,但只能有一个public修饰的类。
* @Param:
* @return:
* @Author: 王海新
* @Date:
*/
class Perosn{
String name;
}
```
上述代码传递对象时,就可以改变了其中的name。可能大家还要些疑惑。之后我们会从对象和内存两个方法再做详细解释。
- this关键字(先打个照面,之后方法继承和多态时讲解。)
### 类和对象的关系
- 类是一种抽象的数据类型,它是对某一类事物整体描述/定义。但并不能够代表某一个具体的事物
- 动物,植物,手机,电脑...(类就是这样,表示一类,但不能代表某一个具体的动物,植物。。。)
- Person类,Pet类,Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
- 对象是抽象概念的具体实例
- 张三就是人的具体实例,张三家的旺财就是狗的一个具体实例。
- 能够体现出特点,展示出功能的是具体的实例,而不是一个抽象的概念
我们可以将这些思想转化为代码实现。
## 3. 对象的创建分析
### 创建与初始化对象
- 使用new关键字创建对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给 创建好的对象进行默认的初始化以及对类中构造器的调用。
- 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有一下两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
- **构造器必须要掌握**
- 类实例化后会返回一个自己的对象!这时才会给这个对象分配空间,初始化以及对类中构造器的调用。
- **以类的方式组织代码,以对象的组织(封装)数据。**
### 构造器详解
- 一个类即使什么都不写,它也会有一个方法。就是构造器。
- 与类名相同,没有返回值 public修饰。
- 一个类可以有多个构造器。但是各个构造器的参数个数,或者类型必须不同。
- **默认有一个空参构造器。如果创建了有参构造器。空参构造器就不会默认创建。如果需要调用无参,需要手动声明一个空参构造器。**
- **使用new关键字创建对象,本质是在调用构造器。**
- 当创建的时候,传递了什么参数,就会调用相同类型的构造器。
- **构造器用来初始化值**
- idea快捷键:alt + insert=》constructor 面里面可以选生成有参或无参的构造器
### 创建对象 内存分析
```java
package com.wang.opp;
/**
* @Auther: 王海新
* @Date: 2021/2/1 12:14
* @Description: 内存分析
*/
public class demo2 {
public static void main(String[] args) {
Pet dog= new Pet();
dog.name = "旺财";
dog.age = 1;
Pet cat= new Pet();
cat.name = "加菲猫";
cat.age = 1;
}
}
/**
* @Description: 动物类
* @Param:
* @return:
* @Author: 王海新
* @Date:
*/
class Pet{
String name;
int age;
publicvoidshot(){
System.out.println("叫了一声");
}
}
```
如以上代码在内存中的分析:
- 栈:
- 3,main方法入栈执行
- 5,dog:引用变量名,引用下一步子啊堆中分配的内存空间
- 堆:
- 6,new Pet() 执行,在堆中分配空间,并初始化值 name:null。age:0
- 7,
```java
dog.name = "旺财";
dog.age = 1;
```
执行。赋值(之前说过,类来操作代码。对象操作数据)
- 8,赋值:name:旺财 age : 1
- 9,(之后可能会回收掉堆中的内存,也有可能不回收)具体收回时机有很多算法控制,后面讲到垃圾回收时会详解。 希望就关注一波吧。蟹蟹!
然后重复4-8步骤创建cat对象的过程。
- 方法区(在堆中):
- 1,程序运行,首先在方法区中加载启动类demo2。同时加载常量及静态方法"旺财" 1"加菲猫" 1
- 4,Pet dog= new Pet();被执行。在方法区中加载Pet类。并加载其中的常量以及方法。name ageshot()
- 静态方法区(在方法区中):
- 2,将main方法加载
### 小结:
1. 类与对象
类是一个模板:抽象。对象是一个具体的实例
2. 方法:
定义,调用!
3. 对象的引用
引用类型
基本类型(8种)
对象是通过引用来操作的:栈--》堆
4. 属性:字段(Field) 成员变量
默认初始化
数字:0 0.0
char:u0000
boolean:false
引用:null
修饰符 属性类型 属性名 = 属性值;
5. 对象的创建和使用
- 必须使用new 关键字创建对象,构造器 Person haixin = new Person();
- 对象的属性: haixin.name
- 对象的方法: haixin.sleep();
6. 类:
静态的属性: 属性
动态的行为: 方法
封装 继承多态
## 4. 面向对象三大特性
### 封装
- 该露的露,该藏的藏
- 我们的程序设计要追求:“高内聚,低耦合”、高内聚就是类的内部数据操作细节自己完成。不允许外部干涉。低耦合:仅少量的方法给外部使用。
- 封装(数据的隐藏)
- 通常。应禁止直接方法一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
- 记住这句话就够了:属性私有,get/set
- 就是在属性上添加private关键字
private String name
- 用private(私有)修饰的关键字与public(共有)相对应,在其它类中无法直接通过方法名.name的方式调用。而需要使用规定的方法。
- get /set就是规定的获取private修饰的属性的方法
```java
class Pet{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
publicvoidshot(){
System.out.println("叫了一声");
}
}
```
如上 调用相应的方法set给属性赋值。用get获取值
```java
/**
* @Auther: 王海新
* @Date: 2021/2/1 12:14
* @Description: 内存分析
*/
public class demo2 {
public static void main(String[] args) {
Pet dog= new Pet();
dog.setName("旺财");
dog.setAge(1);
Pet cat= new Pet();
dog.setName("加菲猫");
dog.setAge(1);
}
}
```
- 为什么要使用封装
因为在设置数据的时候,有些数据是不合法的。我们可以通过封装时,在方法中设置一些检测。来使得我们的数据更安全。
1. 提高程序的安全性,保护数据
2. 隐藏代码实现细节
3. 统一接口
4. 系统可维护性增加了
- 封装的set/get方法也可以重载。其实判断两个方法是否相同就参考两个点:方法名和参数列表
### 继承
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
- extend的意思是“扩展”。子类是父类的扩展。
- JAVA中只有单继承,没有多继承!一个儿子只能有一个爸爸。一个爸爸可以有多个儿子
- 继承是类和类之间的关系。除此之外类和类之间的关系还有依赖,组合,聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类用extends来表示
- 子类和父类之间,从意义上讲应该具有is a 的关系。
- 私有的东西无法被继承
快捷键:Ctrl+ h 可以查看当前类的继承关系
- object类
object类是所有类的父类。我们平时定义的类都默认继承了object类。所以我们刚刚定义的类不写任何东西。在使用其对象的时候,也会有很多方法 属性 可以直接使用。
- super
当子类重载了父类的方法或者变量的时候。用super来表示调用的是父类的。默认是调用子类的。也可以用this.来表示当前类的东西。
super.name 就表示调用的是父类的name
- 如果父类私有的(private修饰)。则也无法调用。
- 同时,super() 表示调用父类的构造器。super(int 1) 就表示调用父类的有参构造器。
- super()在子类的构造器第一行,如果调用this(有参或无参)也需要在第一行。
- 默认调用了父类的无参构造器。并且super必须在第一行。如果父类没有无参构造器就会报错(所以前面我们要求,在写了有参构造器之后,手动写一个无参构造器。避免之后出错)
- 通过在构造器中打印语句。我们知道先执行了父类 再执行子类
- super注意点:
1. super调用父类的构造方法,必须在调用方法的第一个
2. super必须只能出现在方法或者构造方法中
3. super和this不能同时调用构造方法!
- VS this
- 代表的对象不同
this:本身调用者这个对象
super: 代表父类对象的应用
- 前提
this:没有继承也可以使用
super:只能在继承条件才可以使用
- 构造方法
this()本身的构造器
super() 父类的构造器
- 重写
A(extend)继承B
- 静态方法和非静态方法区别很大
假如入A和b都定义了public static void test(){
}方法
A a = new A();
a.test();//结果打印A 中的test
B b = new A();
b.test();//执行的是B中的test
假如入A和b都定义了public void test(){
}方法
A a = new A();
a.test();//结果打印A 中的test
B b = new A();//父类的引用指向了子类
b.test();//结果打印A 中的test
**总结:只有在非静态方法中才能实现真正的重写,静态方法是重写不了的。**
**非静态的不是私有的方法 才可以重写**
idea有提示,只有在打断点的位置出现一个O加一个向上的箭头,才表示重写成功了
- 快捷键:alt+insert 中的override method。可以自动在子类中生成父类的重写方 法
- 重写和重载:
重写是指重写父类中的
重载是指本 类中方法名相同,参数列表不同
- 重新:需要有继承关系,子类重写父类的方法
1. 方法名必须相同
2. 参数列表必须相同
3. 修饰符:范围可以扩大但不能缩小:(修饰符这部分我们还没有全部讲到,大家可以这样记忆。子类的修饰符权限必须大于等于父类)
public>Protected>Default>private
4. 抛出异常:范围:可以被缩小但不能扩大:(子类的异常范围只能等于小于父类)
ClassNotFoundExeception == >Exception(大)
重写,子类的方法和父类的方法必须要一致:方法体不同!
- 为什么要重写:
父类的功能,子类不一定都需要,或者不一定都满足
Alt + insert =》override
### 多态
动态编译:类型:可扩展性
- 多态注意事项:
1. 多态是方法的多态,属性没有多态
2. 父类和子类,有联系 类型转换异常!ClasCastException!
3. 存在条件:继承关系,方法需要重写,父类引用指向子类对象!
4. Father f1 = new son();
5. 不可以继承的方法和常量(无法继承,自然没办法实现多态)
1. static 方法:属于类,它不属于实例
2. final 常量
3. private方法
- 多态
- 既同一方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的,但可以指向对象的引用类型的类型有很多种(父类,有关系的类)
- 多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态性
- instanceof (类型转换)引用类型
```java
package com.wang.opp.demo3;
/**
* @Auther: 王海新
* @Date: 2021/2/2 16:27
* @Description:
*/
public class Persion {
publicvoid run(){
System.out.println("Persion");
}
}
```
```java
package com.wang.opp.demo3;
/**
* @Auther: 王海新
* @Date: 2021/2/2 16:27
* @Description:
*/
public class Student extends Persion {
@Override
public void run() {
System.out.println("Student");
}
public void eat() {
System.out.println("吃");
}
}
```
```java
package com.wang.opp.demo3;
/**
* @Auther: 王海新
* @Date: 2021/2/2 16:26
* @Description: 启动类
*/
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的
//new Student
//new Student
Student student = new Student();
//可以指向的引用类型就不确定了:父类的引用指向子类
Persion persion = new Student();
Object object = new Student();
//对象能执行那边的方法主要看对象左边的类型:和右边关系不大
student.run();
//父类型可以调用子类型,但是不能调用子类独有的方法。
persion.run();//结果都是执行子类重写后的方法。
}
}
```
- 类型转换,我们可以通过类型转换instanceof,来判断两个类是否可以进行强制转换
A extends B
C extends B
Object object = newA();
sout(object instanceof A);//true
sout(object instanceof Object );//true
sout(object instanceof B);//true
sout(object instanceof C);//flase
object的引用指向的是A,和C并没有直接关系。所以打印结果为false。但是不报错(因为C的父类的父类也是Object
sout(A Iinstanceof B);
//直接报类型转换错误。因为A和B并没有一点关系
**我们可以通过instanceof是否编译通过,判断两者是否有父子关系**
- 类型转换:
- 子类可以直接转换为父类。父类转换为子类。需要强转
- 子类转换为父类(强转),会丢失自己独有的一些方法!父类转化为子类,会丢失被子类重写的方法(执行子类中的重写方法)。
- 方便方法的调用,将一些类转换就可以调用不同的方法。减少重复的代码!简洁
抽象:继承 封装 多态 面向对象的三大基本特征都是为了抽象
抽象也是编程思想
**static关键字详解:**
```java
package com.wang.opp.demo4;
/**
* @Auther: 王海新
* @Date: 2021/2/3 17:16
* @Description: 静态代码块
*/
public class Persion {
public Persion() {//3.第三次执行,每次调用都执行
System.out.println("构造函数");
}
{//2,第二执行,每次调用都执行。一般用来赋初始值
System.out.println("匿名代码块");
}
static {//1.最先执行,且仅执行一次
System.out.println("静态代码块");
}
public static void main(String[] args) {
Persion persion = new Persion();
System.out.println("= ============");
Persion persion1 = new Persion();
}
}
```
- 静态导入包
```java
package com.wang.opp.demo4;
//静态导入包
import static java.lang.Math.random;
/**
* @Auther: 王海新
* @Date: 2021/2/3 17:26
* @Description: 静态导入包
*/
public class Student {
public static void main(String[] args) {
//输出一个随机数
System.out.println(Math.random());
//静态导入包之后 就可以直接用
System.out.println(random());
}
}
```
同时,用final修饰的类,不可以再被继承。
### 抽象类
```java
package com.wang.opp.demo5;
/**
* @Auther: 王海新
* @Date: 2021/2/3 17:41
* @Description: 抽象类,因为单继承的原因,抽象类用的并没有下面要讲的接口用的多
*/
public abstract class Action {
public abstract void run();
public void say(){
System.out.println("hah");
}
/*
抽象类就是一个约束。只定义方法名字。不定义具体实现。
*/
}
```
```java
package com.wang.opp.demo5;
/**
* @Auther: 王海新
* @Date: 2021/2/3 17:41
* @Description:
*/
//继承抽象类就必须要重写抽象类中的抽象方法。除非子类也是一个抽象类
public class a extends Action{
@Override
public void run() {
}
}
```
1. 不能new这个抽象类,只能靠子类去实现它:就是一个约束
2. 抽象类中可以编写普通的方法
3. 抽象方法必须再抽象类中
- 思考题 :
- 抽象类有构造器吗? 有!因为抽象类也属于类。我们可以通过手写一个查看,或者通过将生成的class文件放idea中反编译。看看是否有构造器
- 抽象类存在的意义 抽象出来,提高开发效率,增强相互协作。
### 接口
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有!
- 接口:只有规范!自己无法写方法。约束和实现分离:面向接口编程
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能...”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。
- **接口的本质是契约,**就像我们人间的法律一样。制定好后大家都要准守。
- OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具体了抽象能力的语言(比如c# c++ java等),就是因为设计模式所研究的,实际上就是如果合理的去抽象。
- 声明类的关键字是class,声明接口的关键字是interface
- 接口中的抽象方法默认都是 public abstract类型的,所以可以省略不用声明。直接写一个 void run(); void say(); 即可
- 接口中的属性默认都是静态常量 public static final修饰。(一般都不在接口中定义常量)
- 接口不能被实例化,接口中没有构造方法
- implements可以是实现多个接口
- 必须要重写接口中的方法
## 6. 内部类
内部类就是在类的内部再定义一个类,比如A类中定义B类,那么B类相对于A类来说就是内部类,而A相对于B来说就是外部类了
- 内部类通过外部类的对象.new
- 内部类可以直接方法外部类的私有变量。但是静态内部类无法访问非静态的外部类私有变量。(因为静态内部类先在内存中加载)
- 一个java类中可以有多个class类,但是只能有一个public class类
- 接口,在另一个类中new接口然后实现里面的方法,
new User(){
//实现User接口中的方法
}
这个类也是匿名内部类
1. 成员内部类 定义在类中的类
2. 静态内部类 定义
3. 局部内部类 定义在方法中的类
4. 匿名内部类 在调用的时候,也就是new的时候。不用将实例保存到变量中。而是直接使用。 学习学习,一直在学习,但是我从来没有坚持下来。。。 前来学习了,最近在学Java 整理的不错,复习了一遍。
页:
[1]