【Java从头开始】第三章 面向对象

第三章 面向对象

3.1 面向对象概述

3.1.1 面向过程和面向对象有什么区别?

从语言方面出发:

	对于C语言来说,是完全面向过程的
	对于C++语言来说,是一半面向过程,一般面向对象的,(C++是半面向对象的)
	对于Java来说,是完全面向对象的

什么是面向过程的开发方式?

	面向过程的开发方式主要特点是:
		注重步骤:注重的是实现这个功能的步骤
			第一步干什么
			第二步干什么
			……
		另外面向过程也注重实现功能的因果关系
			因为有A所以B
			因为有B所以C
			……

面向过程当中没有对象概念,只是实现这个功能的步骤以及因果关系

面向过程有什么缺点?

		面向过程主要是每一步和每一步之间的因果关系,其中A步骤因果关系到B步骤,A和B联合起来形成一个子模块,子模块和子模块之间又因为因果关系结合在一起,假设其中任何一个因果关系出现问题(发生错误),此时整个系统的运转都会出现问题(因为代码和代码之间的耦合度太高了,扩展性太差了)

关于耦合度:

		螺栓和螺母拧在一起:耦合度高吗?
			这个耦合度低,因为螺栓和螺母可以分开(它们之间是有接口的)
		螺栓和螺母拧在一起后,再用焊条焊接在一起,耦合度高吗?
			这个耦合度很高,耦合度就是粘连程度
			往往耦合度高,扩展性就差

面向过程有什么优点呢?

对于小型项目,采用面向过程的方式进行开发,效率很高,不需要前期进行对象的提取,模型的建立,采用面向过程方式可以直接开始干活,一上来直接写代码,编写因果关系,从而实现功能。

什么是面向对象的开发方式?

	采用面向对象的开发方式,更符合人类的思维方式,人类就是以“对象”的方式去认识世界的
	所以面向对象更容易让我们接受
	面向对象就是将现实世界分割成不同的单元,然后每一个单元都实现成对象,然后去动一下,让各个对象之间协作起来形成一个系统
	
	采用面向对象的开发方式:耦合度低,扩展力强

3.1.2 面向对象使用的术语(三大思想)

	a. OOA:面向对象分析(Object-Oriented Analysis)
	b. OOD:面向对象设计(Object-Oriented Design)
	c. OOP:面向对象编程(Object-Oriented Programming)

3.1.3 面向对象包括3大特征

封装、继承、多态

3.1.4 类和对象的概念

	什么是类:
		“类”实际上在现实生活中是不存在的,是一个抽象的概念,是一个模板,是人类大脑进行思考总结抽象的一个结果。
		类本质上是现实世界当中某些事物具有相同特征,将这些共同特征提取出来形成的概念就是一个“类”,“类”就是一个模板
		
	什么是对象:(另一个名字叫“实例”)
		对象是实际存在的个体
		在java语言中,要想得到“对象”,就必须先定义一个“类”,“对象”是通过“类”这个模板创造出来的
			类就是一个模板:类中描述的是所有对象的“共同特征信息”
			对象就是通过类创建出的个体
			通过类创建对象的过程叫做“实例化”
			多个对象具有共同特征,进行思考总结抽取共同特征的过程叫做“抽象”

3.1.5 类 = 属性 + 方法

属性来源于:状态
方法来源于:动作

3.1.6 类的定义

a. 怎么定义一个类,语法格式是什么?
【修饰符列表】 class 类名{
	//类体 = 属性 + 方法
	//属性在代码上以“变量”的形式存在(描述状态)
	//方法描述动作/行为
}
注意:修饰符列表可以省略
b. 为什么属性是以“变量”形式存在的?
因为属性对应的是“数据”,数据在程序中只能放到变量中,结论:属性其实就是变量

变量的分类:
	1. 方法体中声明的变量:局部变量
	2. 方法体外声明的变量:成员变量(这里的成员变量就是“属性”)

3.2 创建对象

3.2.1 创建对象内存分析

3.2.1.1 栈
栈的特点:先进后出的特点,存储速度比较快(比堆快) 
栈存储速度快的原因:
    栈内存通过栈指针来创建空间与释放空间
    指针向下移动,会创建新的内存,向上移动会释放这些内存
    这种方式速度很快,仅次于PC寄存器
    (要明确移动的大小和范围)
    明确了大小和范围是为了方便指针的移动,但是限制了数据结构的大小,影响了程序的灵活性
    对于更大部分,大小不确定的数据(对象)存储到堆中
    
栈内存中存储的内容:
    1. 基本数据类型
    2. 引用数据类型数据的引用(对象、数组、字符串的引用)
3.2.1.2 堆
存放的是类对象
    
堆内存和栈内存的不同点:我们在创建对象的时候,不必关心堆内存里需要开辟多少的存储空间,不需要关注内存占用的时长(堆内存中内存的释放是由GC完成的)
    
垃圾回收的规则:
    当栈内存中不存在此对象的引用的时候,就将其视为垃圾,等待垃圾回收器回收

3.3 构造方法

3.1.1 构造方法设计

建议每创建一个类,都手动写上去无参的构造方法。不依赖程序的自动生成。

3.4 方法重载

相同的方法名,不同的参数列表(参数类型和参数的数量)。

方法签名:方法名+参数列表(唯一的确定了一个方法),和返回值没有关系

方法重载属于静态多态

3.5 构造方法的重载

一个类中可以有多个构造方法,可以使在不同的创建对象的需求下,调用不同的构造方法来完成对象的初始化

一定要提供一个无参构造方法

3.6 匿名对象

没有对象名称的对象就是匿名对象

匿名对象只能使用一次,因为没有任何引用指向它,很快就会被垃圾回收掉

class Calculator{
    int sum(int x, int y){
        return x + y;
    }
}

//匿名调用对象中的方法
int sum = new Calculator().sum(1,2); //可以直接在构造函数后面调用该类中的实例方法

3.7 封装

封装的意义在于:

  1. 保护或则和防止代码被自己无意中破坏
  2. 保护成员属性,不让类意外的程序直接访问和修改
  3. 可以通过对set方法的限制,约束输入的值,使其在合理范围内

封装的原则:隐藏对象的属性和实现细节,仅对外公开访问方法,并且控制访问级别

建议以后的开发中,对所有的属性进行封装,并为其设置setter和getter方法

3.8 this关键字

this可以完成的操作:

  1. 调用类中的属性

  2. 调用类中的方法或者构造方法(在一个构造方法中调用另一个构造方法)

    规则:当在构造函数中调用另一个构造函数的时候,this() 必须放在第一行

  3. 表示当前对象

3.9 static 关键字

静态属性在内存中的分布:

image-20201222112722179

static表示静态:可以用来修饰成员变量和成员方法,被修饰后就变成了静态变量和静态方法

static的主要作用:创建独立于具体对象的域变量和方法。静态变量存在于方法区中,随着类加载被初始化(成员变量在对象创建的过程中被初始化)

static变量的特点:static变量在内存中只有一处,存在方法区中,不会因为对象的多次创建而建立多个副本

static方法的特点:静态方法被调用的时候,有可能对象还没有被创建

static注意:静态上下文中不能有非静态(因为此时可能对象还没有创建),非静态上下文中可以有静态(此时确定对象已经创建)

3.10 包介绍

  1. 将功能相似或者相关的类或者接口组织在同一个包中,方便类的查找和使用
  2. 不同的包中的类的名字可以是相同的,调用两个包中的同名类时,应该加上包名以区分
  3. 包有访问权限,拥有包访问权限的类才能访问某个包中的类

import 关键字: import 包名.类名

普通的java代码中的方法和类不需要导包,因为它们都定义在java.lang包中,这个包中都是java最常用的方法

3.11 代码块

普通代码块
{
    int a = 10;
    System.out.println(a);
}

构造代码块:
public class demo {
    public static void main(String[] args) {
        Person person = new Person();
    }
}

class Person{
    private String name;
    private int age;
    
    // 以下就是构造代码块:随着对象的每次创建,执行一次,且执行在构造方法之前
    //无论用户调用哪一个构造方法创建对象,构造代码块都必然执行
    {
        System.out.println("对象创建的时候执行");
    }
    
    //以下是静态代码块,在类加载的时候执行
    static {
        System.out.println("静态代码块在类加载时执行");
    }

    public Person(){
        System.out.println("对象创建的时候执行");
    }
}
面试题:
构造方法和构造代码块和静态代码块的执行顺序:
静态代码块---> 构造代码块 ---> 构造方法

3.11 子类继承

Java中只有单继承,多重继承,没有多继承

继承的注意事项:

  1. 父类的public,protected属性和方法可以被子类访问

3.11.1 super关键字

通过super关键字,可以访问父类的构造方法、属性、构造方法

当调用子类的构造方法时,子类在默认调用super(),如果此时父类没有无参构造方法,调用就会出错(super()必须在子类构造方法的第一行)

以下行为不合逻辑:
    //子类构造方法:
    public Son(){
    	this();
    	super();//this()和super()两个必须都在构造方法的第一行
	}

只能是这样:
    public Son(){//super()放在一定会执行的构造方法中
    	this(i);
	}

	public Son(int i){
        super();
    }

3.11.2 重写

重写的规则:

  1. 参数列表必须与被重写的方法相同
  2. 返回类型必须完全与被重写方法的返回类型相同
  3. 访问权限不能比被重写方法的访问权限更低
  4. 父类的成员方法只能被它的子类重写
  5. static方法和private方法不能被重写,但是可以被再次声明

3.11.3 final 关键字

final 修饰属性、变量、类、方法
final修饰属性和变量的时候:
    1. 变量成为了常量,不能再次进行赋值
    2. final修饰的局部变量,只能赋值一次(可以先声明后赋值)
    3. final修饰的是成员变量,必须在声明时赋值
final用于修饰类的时候:
    1。不可以被继承
final用于修饰方法:
    1. 不能被子类重写
    
全局常量:(public static final)可以被任何位置直接使用,且不可改变
常量的命名规范:由1个或多个单词组成,单词和单词之间必须使用下划线隔开,单词中的所有字母大写

3.12 抽象类

3.13 接口

3.13.1 面向接口的编程思想

优点:

  1. 降低程序的耦合性
  2. 易于程序的扩展
  3. 有利于程序的维护

3.13.2 全局常量和抽象方法的简写;

  1. 全局常量编写:

    public static final String INFO = "info";
    |
    |
    v
    String INFO = "info";
    
  2. 抽象方法编写:

    public abstract void print();
    |
    V
    void print();
    

3.13.3 接口和抽象类的区别

  1. 抽象类要被子类继承,接口要被实现
  2. 接口只能写抽象方法,抽象类中可以有实现方法
  3. 接口定义的变量只能时公共的静态的常量,抽象类中的变量是普通变量
  4. 抽象类无法多继承,接口可以多实现
  5. 接口不能有构造方法,抽象类可以有

3.14 多态

3.14.1 多态的体现

方法的重载和重写都是多态的一种:

重载:一个类中方法的多态性体现,静态的实现多态

重写:子父类中方法的多态性体现

3.14.2 多态的使用:对象的类型转换

向上转型:将子类实例转换成父类对象

Person p = new Student();

向下转型:将父类实例转化成子类对象

Student s = (Student) p;

注意:向下转型的过程中,因为父类可能有多个不同的子类,需要提前判断这个父类是否可以被转为当前的具体子类:

if(p instanceof Student){
    Student s = (Student) p;
}

3.15 Object类

3.15.1 toString方法

建议重写toString方法,可以返回对象的字符串表示形,更详细的描述类中的属性。如果不重写,就会直接使用Object的toString方法,返回对象的内存地址:

getClass().getName() + "@" + Integer.toHexString(hashCode());

3.15.2 equals方法

image-20201223101002988

3.16 内部类

概念:广泛意义上内部类分为四种:

  1. 成员内部类
  2. 局部内部类
  3. 匿名内部类(局部内部类的一种)
  4. 静态内部类(成员内部类+static)

3.16.1 成员内部类

成员内部类的特点:

  1. 可以无条件访问外部类中的所有成员属性和成员方法(包括private成员和static成员)
  2. 如果外部类和内部类有同名成员,外部成员会隐藏(就近原则)

成员内部类的创建:

Outter o = new Outter();
Outter.Inner inner = o.new Inner();

3.16.2 局部内部类

特点:

  1. 局部内部类定义在一个方法或者一个作用域里面,其访问权限仅限于访问方法内或者该作用域内
  2. 就像方法中的局部变量一样,不能有public、protected、private和static修饰符

3.16.3 匿名内部类

特点:匿名内部类没有名字,创建的时候必须继承一个父类或者实现一个接口(只能二选一

    final int a = 0;
    public static void main(String[] args) {
        int a = 10;
        something(new Person() {
            @Override
            public void say() { //重写了Person接口中的say方法
                System.out.println(a);
            }
        });

    }

    public static void something(Person p){
        p.say();
    }

注意:

  1. 匿名内部类中不能有构造函数
  2. 匿名内部类中不能存在任何static成员
  3. 局部内部类的所有限制
  4. 匿名内部类不能是抽象的,因为必须要实现所有的继承过来的抽象方法
  5. 只能访问final修饰的局部变量

image-20201223111900946
只能访问所在域的final类型的变量,final可以省略,但是定义的变量内容不能更改

为什么不能访问非final修饰的局部变量呢?

1. 在编译内部类(以下代码)的时候(内部类会被单独的编译成字节码文件):
    	int a = 10;
  	    something(new Person() {
            @Override
            public void say() {
                System.out.println(a);
            }
        });  
内部类中会生成一个变量来copy 局部变量a中的102. 编译成字节码之后,内部类会被单独存储(此时内部的备份就是103. 如果外面的a发生了更改
4. 在使用内部类的时候就会出现内外不一致的情况(内部使用的是10,外部使用的不是10

3.16.4 静态内部类

特点:

  1. 被static修饰
  2. 不需要依赖于外部类对象,和静态属性有些类似
  3. 不能使用外部类的非static成员(静态上下文中不能有非静态的内容,因为那些内容只有创建对象后才能使用)
  4. 可以做到在没有创建外部类的时候,就创建内部类

创建:

Book.Info bi = new Book.Info();

3.17 包装类

为了解决8中基本数据类型不是对象的问题,引入了八种基本数据类型的包装类

包装类的分类:

  1. Number:Integer, Short, Long, Double, Float, Byte 都是Number的子类,表示是一个数字
  2. Object:Character,Boolean都是Object的直接子类

JDK1.5引入了自动装箱和拆箱

3.18 方法的可变参数

JDK1.5之后引入

    public static int sum(int ... nums){//此处可以输入任意数量的参数
        int n = 0;
        for (int i = 0; i < nums.length; i++) {
            n+=nums[i];//参数在方法内部用数组接收
        }
        return n;
    }

注意:可变参数只能出现在参数列表的最后

3.19 递归

image-20201223165520996

3.20 异常

3.20.1 异常是如何产生的

1. 发生了异常,JVM根据异常的情况,创建了一个异常的对象,包含了异常的信息
2. main方法没有对这个异常进行处理,自动将异常抛给了main的调用者JVM
3. JVM对异常信息进行了相应(将异常信息显示到控制台,中断处理)

3.20.2 如果想要人工捕获异常,就需要使用try…catch代码块

try{
    //有可能发生异常的代码段
}catch(异常类型1 对象名1){
    //异常处理的操作
}catch(异常处理2 对象名2){
    //异常处理操作
}finally{
    //异常的统一出口
}

try catch处理流程

1. 一旦产生异常,JVM会自动产生一个异常类的实例化对象
2. 如果异常发生在try语句,会自动找到匹配的catch语句执行,如果没有在try语句中,就会抛出给调用者
3. 所有的catch根据catch方法的参数匹配异常类的实例化对象,如果匹配成功,才进入catch语句块进行处理

异常被捕获处理之后,代码不会在此中断,会继续向下运行

finally——异常的统一出口

try{
    
}catch(){
    
}finally{
    //必然执行的异常统一处理出口
    //无论是否发生异常,finally必然执行
    //比如资源的释放
}

面试问题:
1. 如果是程序被关闭了(JVM停机,停电了),finally才不会执行,否则一定会执行
2. 如果try或者catch中有returnfinally如何执行?
    * 先计算返回值,将返回值存储起来,等待返回
    * 执行finally代码块
    * 将之前存储的返回值返回(即是finally对这个值造成了改变,返回值依旧不变)
3. 如果finally中有return,则程序会在finally中退出(finally中的return会覆盖trycatch中的finally

3.20.3 throws

什么时候使用throws:

当方法产生的异常是由于传参导致的,而方法本身的逻辑是没有问题的,可以将异常抛出给调用者
如果最后抛给了JVM,程序就会中断

throw关键字:

在程序运行中人工抛出异常:
    throw new RuntimeException("你的操作有误")

3.20.4 自定义异常类

如果是受检异常(Exception的直接子类),一定要抛出,不能用try catch,(不能自己抛出异常,又接受处理,没有意义)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值