面向对象入门(类和对象)
1.面向对象和面向编程思想
-
面向过程编程思想: 强调的是过程, 必须清楚每一个步骤,然后按照步骤一步一步去实现
-
面向对象编程思想:强调的是对象, 通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。类的概述
2.类与对象
-
类是用来描述一类具有共同属性和行为事物的统称。所以其实类在客观世界里是不存在的,是抽象的,只是用来描述数据信息的。
-
类的组成:
- 属性----共同拥有的
- 行为----共同拥有的
-
对象是类的一个实例(并不是你的女朋友哈),具体存在的,看得见摸得着的,并且具备该类事物的属性和行为
- 对象的属性:对象的属性具有特定的值
- 对象的行为:对象可以操作的行为
- 类和对象的关系
-
类和对象的关系
-
类是对一类具有共同属性和行为的事物的统称,是抽象的
-
对象是一类事物的具体实例,看得见,摸的着的,真实存在的实体,是具体的
-
类是对象的抽象,对象是类的实体
-
3.成员变量默认值
-
成员变量
变量类型 整数 浮点型 布尔型 字符型 引用类型 默认值 0 0.0 false 不可见字符 ‘\u0000’ null -
局部变量: 定义在方法中的变量
- 局部变量没有默认值
4.对象内存图
- jvm首先会调用main方法来执行程序
- java程序第一次使用某个类,就会把该类的字节码文件(.class)加载到方法区
- 凡是new就会在堆区开辟一块新的空间
- 方法一旦被调用就会被加载到栈区,开辟一块新的空间,来执行该方法
- 方法执行完毕后,就会弹栈(回收方法执行占用的栈空间)
- 对象是根据类来创建的,类有什么对象就有什么
- 堆区中的对象,如果没有变量指向,那么垃圾回收机制会在合适的时间进行回收该对象占用的空间
-
多个对象内存
-
多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自对象的内存区域中,成员方法多个对象共用的一份
-
凡是new就会重新在堆区开辟一块新空间
-
对象和对象之间的关系是相互独立的
-
-
多个变量指向相同对象
- 当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)
- 只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。
5.对象属性封装
- 关键字private
- 被private修饰的成员变量或者成员方法,只能在本类中访问
- 为什么要对属性进行封装
- 通过对象名直接访问成员变量的方式来对属性赋值,会存在数据安全隐患
- set和get方法
- 由于属性使用了private关键字修饰,在其他类中无法直接访问,所以得提供公共的访问方法,我们把这张方法叫做set和get方法,用public修饰
- this关键字: this代表当前调用方法的引用,哪个对象调用this所在的方法,this就代表哪一个对象
- this关键字其主要作用是区分同名的局部变量和成员变量
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
- this关键字其主要作用是区分同名的局部变量和成员变量
6.封装
概述:是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
原则: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
好处:
- 通过方法来控制成员变量的操作,提高了代码的安全性
- 把代码用方法进行封装,提高了代码的复用性
7.构造方法
-
构造方法是一种特殊的方法,主要是完成对象的创建和对象数据的初始化
- 构造方法的写法上,方法名与它所在的类名相同
- 构造方法没有返回值,所以不需要返回值类型,甚至不需要void
- 构造方法可以重载
-
构造方法的创建
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
8.匿名对象:指"没有名字"的对象。
- 特点: 匿名对象只能使用一次
9.继承
-
定义: 在java中指的是“一个类”可以“继承自”“另一个类”。 "被继承的类"叫做: 父类/超类/基类,"继承其他类的类"叫做:子类。继承后,“子类”中就“拥有”了“父类”中所有的成员(成员变量、成员方法)。 “子类就不需要再定义了”。
-
好处:通过继承可以将一些共性的属性,行为抽取到一个父类中,子类只需要继承即可
- 提高代码的复用性(减少代码冗余,相同代码重复利用)。
- 使类与类之间产生了关系。
-
用法:
通过
extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:class 父类 { ... } class 子类 extends 父类 { ... }
需要注意:Java是单继承的,一个类只能继承一个直接父类,并且满足is-a的关系,例如:Dog is a Animal, Student is a Person
-
访问规则:
- 父类的构造方法不能被子类继承
- 父类的“私有成员”可以被子类继承,但子类不能被直接访问。
- 当通过“子类”访问非私有成员时,先在子类中找,如果找到就使用子类的,找不到就继续去“父类”中找。
-
特点:
- 类的继承只能是单继承,不能多继承,但是可以多层继承
- java中所有类都是直接或者间接继承Object,所有类都是Object类的子类
-
继承体系内存图原理—父类空间优先于子类对象产生
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。
10.方法重写
- 子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
- 注意:
- 方法重写是发生在子父类之间的关系。
- 子类方法重写父类方法,返回值类型、方法名和参数列表都要一模一样。
- 子类方法重写父类方法,必须要保证权限大于等于父类权限。
- 访问权限从大到小: public protected (默认) private
11.this和super关键字
this | super | |
---|---|---|
访问范围 | 本类的成员属性、成员方法、构造方法 | 父类的成员属性、成员方法、构造方法 |
只能在本类的构造方法中使用this调用其他构造方法 | 子类的构造方法默认会调用父类的空参构造方法 | |
在本类的构造方法中使用this调用其他构造方法,必须放在该构造方法的第一行,否则会报错 | super访问父类的构造方法,可以用来初始化从父类继承过来的属性 | |
两个构造方法不能使用this同时相互调用 | 在子类的构造方法中,使用super调用父类的构造方法,必须放在子类构造方法的第一行 |
- super访问成员变量和成员方法: 优先去父类中找,如果有就直接使用,如果没有就去爷爷类中找,如果有,就用,依次类推…
- 子类的构造方法默认会调用父类的空参构造方法,如果父类中的没有空参构造方法,只定义了有参构造方法,会编译报错
- 子类构造方法中使用super调用父类的构造方法,是为了在创建子类对象的时候,初始化从父类继承过来的属性
12.抽象类
- 概述: 使用abstract关键字修饰的类就是抽象类
- 特点: 这种类不能被创建对象,它就是用来做父类的,被子类继承的
格式:
修饰符 abstract class 类名{
成员变量
构造方法
成员方法
抽象方法
}
-
抽象方法: 没有方法体,使用abstract修饰的方法就是抽象方法
- 使用场景:如果父类中某个方法,所有子类都有不同的实现,那么就可以把该方法定义为抽象方法
- 作用: 强制要求子类重写的
-
注意事项:
- 抽象类不能被创建对象,就是用来做“父类”,被子类继承的。
- 抽象类不能被创建对象,但可以有“构造方法”——为成员变量初始化。
- 抽象类中可以没有抽象方法,但抽象方法必须定义在抽象类中
- 子类继承抽象类后,必须重写抽象类中所有的抽象方法,否则子类必须也是一个抽象类
13.模板设计模式
- 针对某些情况,在父类中指定一个模板,然后根据具体情况,在子类中灵活的具体实现该模板
public abstract class Person{
// 有方法体的方法: 通用模板
public void sleep(){
System.out.println("两眼一闭,就睡觉...");
}
// 没有方法体的方法(抽象方法): 填充模板(要子类重新实现的)
public abstract void eat();
}
- 抽象类体现的就是模板设计思想,模板是将通用的东西在抽象类中具体的实现,而模板中不能决定的东西定义成抽象方法,让使用模板(继承抽象类的类)的类去重写抽象方法实现需求
模板模式的实现步骤
- 定义抽象父类作为模板
- 在父类中定义"模板方法"— 实现方法(通用模板)+抽象方法(填充模板)
- 子类继承父类,重写抽象方法(填充父类的模板)
- 测试类:
- 创建子类对象,通过子类调用父类的“实现的方法”+ “子类重写后的方法”
14.final关键字
final: 不可改变。可以用于修饰类、方法和变量。
- 类:被修饰的类,不能被继承。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,就只能赋值一次,不能被重新赋值。
修饰变量
局部变量——基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
局部变量——引用类型
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改。
public class FinalDemo {
public static void main(String[] args) {
// 引用类型
final Student stu = new Student("张三",18);
//stu = new Student("李四",19);// 编译报错
stu.setAge(19);
}
}
成员变量
成员变量涉及到初始化的问题,初始化方式有两种,只能二选一:
-
显式初始化;
public class FinalVariable { final int NUM1 = 10; }
-
构造方法初始化。
public class FinalVariable { final int NUM2; public FinalVariable(int NUM2){ this.NUM2 = NUM2; } public FinalVariable(){ this.NUM2 = 10; } } //所有的构造方法都必须为其初始化
被final修饰的常量名称,一般都有书写规范,所有字母都大写。
15.static关键字
static是一个静态修饰符关键字,表示静态的意思,可以修饰成员变量和成员方法以及代码块。
当 static
修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
- static修饰成员方法
被static修饰的方法会变成静态方法,也称为类方法,该静态方法可以使用类名直接调用。
- 静态方法调用的注意事项:
- 静态方法中不能出现this关键字
- 静态方法中只能直接访问静态成员变量和静态成员方法
- 静态方法中不能直接访问非静态成员变量和非静态成员方法
- 非静态方法中可以直接访问一切成员变量和成员方法
16.接口
接口是Java语言中的一种引用类型,是方法的"集合",所以接口的内部主要就是定义方法,包含常量,抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(jdk9)
引用数据类型:数组,类,接口。
- 接口是java语言中的一种引用数据类型
- 接口中的成员:
接口中的成员 | 格式 | 访问特点 |
---|---|---|
常量(jdk7及其以前) | public static final int NUM1 = 10; | 主要是供接口直接使用 |
抽象方法(jdk7及其以前) | public abstract void method1(); | 供实现类重写的 |
默认方法和静态方法(jdk8额外增加) | public default void method3(){} public static void method4(){} | 默认方法:供实现类继承的 (实现类中可以直接调用, 实现类对象也可以直接调用) 静态方法: 只供接口直接调用,实现类继承不了 |
私有方法(jdk9额外增加) | private static void method5() private void method6() | 只能在接口中直接调用,实现类继承不了 |
- 定义接口使用interface关键字—接口编译后产生class文件
- 接口不能创建对象,需要使用实现类实现接口(类似于继承),实现接口的类叫做实现类(子类)
实现接口:类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。 实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
- 类可以实现一个接口,也可以同时实现多个接口。
- 类实现接口后,必须重写接口中所有的抽象方法,否则该类必须是一个“抽象类”。
- 类可以在“继承一个类”的同时,实现一个、多个接口;
- 额外的功能—> 在接口中定义,让实现类实现
- 如果可以确定的通用功能,使用默认方法
- 如果不能确定的功能,使用抽象方法
- 共性的功能—> 在父类中定义,让子类继承
- 如果可以确定的通用功能,使用默认方法
- 如果不能确定的功能,使用抽象方法
17.接口和接口的关系以及接口的冲突
接口和接口的关系:
接口可以“继承”自另一个“接口”,而且可以“多继承”。
interface IA{}
interface IB{}
interface IC extends IA,IB{//是“继承”,而且可以“多继承”
}
冲突类型 | 接口多实现时 | 接口多继承接口 |
---|---|---|
公有静态常量的冲突 | 如果多个接口中有相同的常量,那么实现类就无法继承 | 子接口无法继承父接口中冲突的常量 |
公有抽象方法的冲突 | 实现类只需要重写一个 | 子接口只会继承一个有冲突的抽象方法 |
公有默认方法的冲突 | 实现类必须重写一次最终版本 | 子接口中必须重写一次有冲突的默认方法,必须加default |
公有静态方法的冲突 | 静态方法是直接属于接口的,不能被继承,所以不存在冲突 | 不冲突,因为静态方法是直接属于接口的,只能使用本接口直接访问 |
私有方法的冲突 | 私有方法只能在本接口中直接使用,不存在冲突 | 私有方法只能在接口中访问,也没有冲突 |
冲突类型 | 实现类继承父类又实现接口时的冲突 |
---|---|
公有静态常量的冲突 | 子类无法继承有冲突的常量 |
公有抽象方法的冲突 | 子类必须重写一次有冲突的抽象方法 |
公有默认方法的冲突 | 优先访问父类的 |
公有静态方法 | 只会访问父类的静态方法 |
私有方法的冲突 | 不存在冲突 |
18.多态
定义
- 多态: 是指同一行为,对于不同的对象具有多个不同表现形式。
- 程序中多态: 是指同一方法,对于不同的对象具有不同的实现.
前提条件【重点】
- 继承或者实现【二选一】
- 父类引用指向子类对象\接口引用指向实现类对象【格式体现】
- 方法的重写【意义体现:不重写,无意义】
多态的体现: 父类的引用指向它的子类的对象:
父类类型 变量名 = new 子类对象;
变量名.方法名();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
多态时访问成员的特点:
- 成员变量:编译看父类,运行看父类(编译看左边,运行看左边)
成员方法:
非静态方法:编译看父类,运行看子类(编译看左边,运行看右边)
静态方法: 编译看父类,运行看父类(编译看左边,运行看左边)
结论:除了非静态方法是编译看父类,运行看子类,其余都是看父类
注意:多态的情况下是无法访问子类独有的方法
多态的形式:
- 普通父类多态
- 抽象父类多态
- 父接口多态
多态的应用场景:
- 变量多态: 如果变量的类型为父类类型,该变量就可以接收该父类类型的对象或者其所有子类对象
- 形参多态:参数类型为父类类型,该参数就可以接收该父类类型的对象或者其所有子类对象
- 返回值多态:如果返回值类型为父类类型,那么就可以返回该父类类型的对象或者其所有子类对象
多态的好处和弊端:
- 好处
- 提高了代码的扩展性
- 弊端
- 多态的情况下,只能调用父类的共性内容,不能调用子类的特有内容。
引用类型转换:
-
向上转型
-
子类类型向父类类型向上转换的过程,这个过程是默认的。
Aniaml anl = new Cat();
-
-
向下转型
-
父类类型向子类类型向下转换的过程,这个过程是强制的。
Aniaml anl = new Cat(); Cat c = (Cat)anl;//向下转型
-
-
注意:
1.向下转型的时候:右边父类类型的变量一定要指向要转型的子类类型的对象
2.不管是向上转型还是向下转型,一定满足父子类关系或者实现关系
instanceof关键字
在引用类型转换的时候很容易出现类型转换异常,所以为了提高代码的严谨性,转型之前得先判断一下
if(变量名 instanceof 数据类型){
}
执行:
判断前面变量指向的对象类型是否是后面的数据类型:
如果前面变量指向的对象类型是属于后面的数据类型,那么就返回true
如果前面变量指向的对象类型不是属于后面的数据类型,那么就返回false
19.内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
-
成员内部类 :定义在类中方法外的类。
定义格式:
class 外部类 { class 内部类{ } }
访问特点
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须要建立内部类的对象。
创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
-
匿名内部类: 是内部类的简化写法。它的本质是一个
带具体实现的
父类或者父接口的
匿名的
子类对象。格式:
对于类: 概述:本质其实就是一个类的匿名子类的对象 格式: new 类名(){ 实现抽象方法 }; 对于接口: 概述:本质是一个接口的匿名实现类的对象 格式: new 接口名(){ 实现抽象方法 }; 使用场景: 如果方法的形参类型为抽象类或者接口类型,那么为了简化代码,可以直接传入该抽象类或者接口的匿名内部类
20.引用类型使用小结
- 类名作为方法参数和返回值:可以直接传入该类的对象;返回该类的对象
- 抽象类作为方法参数和返回值:只能传入该类的子类对象;返回该类的子类对象
- 接口作为方法参数和返回值:只能传入该接口的实现类对象;返回该接口的实现类对象
传递的都是地址值,返回的也是地址值
- 类作为成员变量 : 赋该类的对象
- 抽象类作为成员变量 ; 赋该类的子类对象
- 接口作为成员变量 : 赋该接口的实现类对象
21.权限修饰符
public | protected | (空的)default | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
-
成员变量使用
private
,隐藏细节。 -
构造方法使用
public
,方便创建对象。 -
成员方法使用
public
,方便调用方法。
22.代码块
-
构造代码块
格式: {} 位置: 类中,方法外 执行: 每次在调用构造方法的时候,就会执行 使用场景: 统计创建了多少个该类对象
-
静态代码块
格式:static{} 位置: 类中,方法外 执行: 当类被加载的时候执行,并只执行一次 使用场景: 例如加载驱动,这种只需要执行一次的代码就可以放在静态代码块中
-
局部代码块
格式:{} 位置: 方法中 执行: 调用方法,执行到局部代码块的时候就执行 使用场景: 节省内存空间,没有多大的意义