面向对象编程
面向对象是一种编程思想
面向对象的概述:以人类习惯的思维方式,用对象来理解和分析问题,使开发软件的方法与过程尽可能接近人类认识的世界、解决问题的思维方法与过程
面对对象编程:强调的是做事情的基本步骤,强调解决问题的方式,面向对象是基于面向过程的
面向过程编程:强调的是做事情的主题,更强调谁来解决问题,更强调的是数据,强调的是谁拥有数据,谁操作数据的权利
面向对象的特点:封装,继承,多态
- 类和对象
类:是一类事物共性的属性和行为
对象:是属性的载体,是行为的执行者
类
概述:定义一组属性和行为的集合
属性:对事物特征的描述,一般是变量
行为:对事物功能的描述,一般是方法
对象
概述:是一个实实在在的个体,是属性的载体,是行为的执行者
创建格式:类名 对象名 = new 类名();
访问格式:
- 属性修改时,对象名.属性名= 属性值;
- 属性访问时,对象名.属性名
- 方法访问时,对象名.方法名();
对象的内存理解
- 首先将要创建对象的所属的类型的字节码文件加载到方法区中
- 在栈内存中创建对象的引用,用来指向对象的地址值
- 在堆内存中开辟一块空间,用来存储对象的地址值
- 对象的引用指向堆内存中的地址值
注意事项:
- 第一次使用某个类型时需要加载字节码文件之后的每次使用都不需要再加载,一个类只加载一次
- 在创建对象的过程中,对象内部会隐含一份所属类型的字节码文件地址。可以通过该地址找到所属类型
- 每一个对象都有一份类的成员变量
- 每次使用new关键字,都会在堆内存中开辟一个新的空间,这就说明每new一次都是不同的对象
成员变量和局部变量
- 定义位置不同:成员变量:定义在类中,局部变量:定义在方法中
- 内存空间不同:
- 成员变量:随着对象创建在堆内存当中
- 局部变量:随着方法创建在栈内存当中
- 内存时间(生命周期)不同:
- 成员变量:属于对象,随着对象的创建而创建,随着对象的销毁而销毁
- 局部变量:属于方法,随着方法的进栈而创建,随着方法的结束而销毁
- 初始化状态不同:
- 成员变量:具有默认初始化值
- 局部变量:不具有初始化值,必须先赋值后使用
内部类
内部类一般包括:成员内部类,局部内部类,匿名内部类,静态内部类
成员内部类
- 成员内部类可以无条件的访问外部类的所有成员属性和成员方法(包括私有成员和静态成员)
class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}
- 当成员内部类中存在和外部类相同的成员变量和方法时,默认在内部类中访问的是内部类中的变量和属性如果要访问外部类中的同名属性或方法,需要用以下方式访问
外部类.this.成员变量
外部类.this.成员方法
- 如果外部类想要访问内部类中的变量和方法时,需要先创建内部类的对象,再通过这个对象的引用进行访问
class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance() {
return new Draw();
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
}
}
}
- 成员内部类是依附外部类的存在而存在的时候,所以要创建内部类的对象应先创建外部类的对象
public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
局部内部类
局部内部类是定义在一个方法或者一个作用域中的类,局部内部类不能够用public ,private,protected以及static修饰符修饰
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
匿名内部类
在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护,匿名内部类在编译的时候由系统自动起名为 Outter$1.class
一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的并且它不能使用外部类的非static成员变量或者方法。
因为在没有外部类的对象的情况下,可以创建静态内部类的对象
创建静态内部类对象的一般形式为: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()
内部类的使用场景和好处
- .每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。
- 2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
- 3.方便编写事件驱动程序。
- 4.方便编写线程代码。
封装
概述:隐藏了事物的实现细节,对外公开的访问方式
- 封装的好处:
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
private关键字
可以修饰:成员变量,成员方法,内部类,构造方法
效果只能在本类中访问
get,set
作用:对外提供了公开的访问方式
set方法设置值
get方法获取值
变量访问规则
变量访问规则是就近原则,如果在局部方法中有和成员变量的同名变量,如果想要访问成员变量,那么要用this.变量名
构造方法
作用:给成员变量赋值
构造方法的格式:
修饰符 方法名称(形式参数){
方法体;
}
静态
概述:static 关键字让所有对象共享一份变量,方法
静态的特点:
- 不会随对象的变化而变化
- 加载时机,随着类的加载而加载
- 优先于对象存在
- 被所有对象共享
调用格式:类名.静态变量名
静态和非静态:
- 概念不同:非静态变量属于对象,静态变量属于类
- 内存空间不同,存储位置不同:非静态变量存储在堆内存中,静态变量存储在方法区的static区中
- 内存时间不同(生命周期不同):非静态变量随着对象的消失而消失,静态变量随着类的结束而消失
- 访问方式不同:所有非静态成员都是通过对象来访问,所有静态成员一般都是通过类来访问
继承
概述:让类与类之间产生关联
继承的优点:提高了代码的复用性,提高代码的可维护性
继承的缺点:提高了代码的耦合性
注意事项:
- 私有的成员不能够被继承
- 父类中的构造方法不能被继承
- 继承要符合is a的逻辑
继承的特点:符合单继承,可以进行多层继承
super和this关键字
super关键字:可以通过super来访问父类中的属性,super用来引用当前对象的父类
this关键字:指向自己的引用
重写
重写的方法权限要么不变,要么越来越大
重写的定义:在子父类当中,方法名称相同,参数列表相同,返回值类型相同
重载的定义:在同一个类当中,方法名称相同,参数列表不同,返回值类型相同
final关键字
final 可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类。
使用 final 关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写:
声明类:
final class 类名{
}
声明方法
修饰符 final 返回值类型 方法名(){
}
代码块
局部代码块
格式:任意一段代码用{}括起来
位置:方法中
作用:限定变量的生命周期
构造代码块
格式:任意一段代码用{}括起来
位置:类中方法外
作用:用于执行所有构造方法中的共性内容,或者给成员变量赋值
在对象的创建过程中,由JVM自动调用针对每个对象有且仅有一次调用机会,如果仅仅是类加载了,但是没有创建对象,那么构造代码块不会运行
静态代码块
格式:
static{
代码段
}
位置:类中方法外
执行特点:随着类的加载而加载,,类只加载一次,所以静态代码块也只加载一次,在最初执行,早于创建对象的一系列操作
作用:给静态变量赋值,或者用于执行一次并且必须在一开始就执行的代码
抽象类
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在 Java 语言中使用 abstract class 来定义抽象类
格式:
修饰符 abstract class 类名{
}
抽象方法:只有方法声明而没有实现的,额外再方法声明上加上abstract关键字
格式:
public abstract void test();
抽象方法所在的类一定是抽象类,抽象类中不一定有抽象方法
非抽象方法可以继承给子类,抽象方法需要在子类中进行实现,除非子类也是抽象类
接口
接口的概述:一切定义规则的事物都是接口
接口的优点:提高的代码的开发效率,降低了代码的耦合度,将方法和实现进行了分离
接口,在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。
Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
接口支持多继承
接口的特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
接口的实现
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
- 一个类可以同时实现多个接口。
- 一个类只能继承一个类,但是能实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
接口的继承使用extends关键字,子接口继承父接口的方法。
标记接口
最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
标记接口的目的:
- 建立一个父接口,比如:把这个标记接口来建立一组接口的父接口,当一个接口继承了标记接口,Java中的JVM就知道该接口要干什么了
- 向一个类中添加数据类型
1.7多态
概述:事物的多种形态
前提:必须有继承,方法的重写,父类引用指向子类对象
访问特点:
- 成员变量:编译看左边,运行看右边
- 成员方法:编译看左边,运行看右边
- 静态方法,编译看左边,运行看左边
在内存中原理:在程序运行过程中,会具体判断当前对象属于哪个类型的对象,对象属于哪个类型,就执行对象所属的方法
多态的优点:提高代码的可扩展性
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法
instanceof
格式:A instanceof B
作用:用来判断A对象是否和B类型有关