面向对象编程三大特性
- 面向对象:以对象为中心的编程思想,通过指挥对象实现具体功能。
- 面对过程:以过程为中心的编程思想,实现具体功能都是自己实现。
类和对象的关系:
类:
-
类是对现实生活类中具有共同属性和行为的实物的抽象。 -
类是对象的数据类型,类是具有相同属性和行为的一组对象的集合。 -
类就是对现实事务的一种描述。
而对象则是以类为模板所创建的。
类的组成
-
*属性:指事物的特征,也就是成员变量。 -
*行为:指事物能执行的操作,也就是成员方法。 -
*构造方法: 方法名与类名一致,没有返回值类型,没有返回值。用于创建对象,没创建一次对象,执行一次构造方法,不能手动调用构造方法。 系统默认为无参构造,如果定义了构造方法,无参构造不存在。
对象创建内存模型
(1)当程序开始运行,方法区以加载好程序所需类的字节码文件。
(2)栈内存加载程序,当运行到需要创建对象时,会在堆内存中创建对象,此时对象的成员变量为初始值,成员方法为地址,指向方法区中的类成员方法。
(3)堆内存中所创建的对象指向栈内存中引用,然后赋值。可以通过对象引用找到堆内存中的成员变量进行赋值,使用成员方法则是通过引用找到堆内存中的成员方法地址,然后在方法区中找到具体的成员方法。
成员变量和局部变量的区别:
(1)位置不同: 成员变量在方法外,局部变量在方法内部或方法声明上
(2)内存位置不同:成员变量在堆内存中,局部变量在栈内存中
(3)声明周期不同:成员变量随对象的存在而存在,局部变量随方法的调用而存在,方法调用结束就消失
(4)初始化值不同:成员变量默认初始化值,局部变量没有默认初始值,必须自定义初始化赋值才能使用。
面向对象三大特性之一———封装
将成员变量隐藏在对象内部,外界无法直接操作,通过该类提供的方法来实现对隐藏信息的操作和访问,封装通过get、set方法的方式来控制成员变量的操作,提高了代码的安全性和复用性。
封装的思想实现类高内聚、低耦合
this关键字
this修饰的变量用于代指成员变量,其主要作用是区分成员变量和局部变量的重名问题。
this的本质其实上是当前调用方法的引用,那个对象调用的方法,this就代表那个对象。
this.:
this()
static关键字
可以修饰成员方法,成员变量。
- 被修饰的方法和变量被类的所有对象共享。
- 随类的加载而加载,优先于对象存在。
- 可以通过类名调用,也可通过对象名调用。
- 静态方法只能访问静态成员,非静态方法可以访问静态成员,也可以访问非静态成员。
- 静态方法中没有this关键字。
- 静态和非静态不能互相访问的原因是因为静态成员在类加载时创建,而非静态成员是在对象实例化的时候才创建。
- 父类的静态方法可以被子类继承,但不能被子类重写。
- 父类的非静态方法不能被子类重写为静态方法。
- this和super在类中属于非静态变量,在静态方法中不能使用。
**面向对象编程三大特征之一——继承
- 使子类具有父类的属性和方法,可以在子类中重新定义,以及追加属性和方法,子类可以使用父类中的非私有成员。
- 继承提高了代码的复用性,多个类的相同的成员可以放在同一个类中。
- 提高了代码的维护性,如果方法的代码需要修改,修改移除即可。
- 类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性、
- 使用继承,需要考虑类与类之间是否存在is a的关系,不能盲目继承。
例如狗、猫是动物的一种,狗和猫都是子类,动物是父类。 - java中类值支持单继承,不支持多继承。但是支持多层继承,子类继承父类,孙类继承子类,孙类就拥有了父类和子类的方法。
- 继承中成员访问特点:采用就近原则。子类局部范围找,子类成员变量找,服装类成员范围找,没有找到就报错。
super关键字
super代表父类储存空间的表示,可以理解为父类对象引用
this.成员变量——访问本类成员变量
super.成员变量——访问父类成员变量
this.成员方法——访问本类成员方法
super.成员方法——访问父类成员方法
this()访问本类构造方法
super()访问父类构造方法
子类会继承父类中的数据,还会使用父类的数据。所以,子类初始化之前,一定会先完成父类数据的初始化,每个子类构造方法的第一条语句默认都是super()
如果父类中没有无参构造方法,只有有参构造方法,该怎么办呢?
1.通过使用super关键字去显示的调用父类的带参构造方法
2.子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法。
this() super()必须放在构造方法的第一行,二者不能共存。
方法重写注意事项
- 私有方法不能被重写,父类中的私有成员子类是不能继承的
- 子类方法的访问权限不能更低
- 静态方法不能被重写
抽象类
- 子类共性功能抽取时,有些方法在父类中并没有体现,这时候就需要抽象类了
- 抽象方法没有方法体,类中有抽象方法,此类必须定义为抽象方法,抽象类中不一定有抽象方法
- 抽象类不能实例化
- 抽象类可以有构造方法
- 抽象类的子类要么重写抽象类中的所有抽象方法,要么是抽象类。
- 抽象类和抽象方法必须使用abstract修饰
提到抽象类就不得不提到模板设计模式
模板设计模式
把抽象类整体看做一个模板,模板中不能决定的东西定义成抽象方法,让使用模板的类(继承抽象类的类)去重写抽象方法实现需求。
模板设计模式已经定义了通用结构,使用者只需要关心自己需要实现的功能即可。
final关键字
- final可修饰——成员方法、成员变量、类
- final修饰的类不能被继承、方法不能被重写、修饰的变量变为一个常量,不能再次赋值
- 如果是基本变量,不能改变的是值
- 如果是引用变量,不能改变的是地址值,但地址内的内容是可以改变的。
局部代码块
方法中定义
- 作用:限定变量的生命周期,及早释放,提高内存利用率
构造代码块
类中方法外定义
- 特点:每次构造方法执行时,都会执行该代码块,并且在构造方法前执行
- 作用:将多个构造方法中相同的代码抽取到构造代码块中,提高代码的复用性。
静态代码块
- 类中方法外定义
- 通过static修饰,随类的加载而加载,只还行一次。
- 在类加载时对一些数据进行初始化操作。
接口
- 接口是一种公共的规范标准,只要符合规范标准,都可以通用。
- 接口是用于定义规范,用来做功能的扩展。
- 成员变量:只能是常量,默认修饰符 public static final
- 构造方法:无
- 成员方法:只能是抽象方法,默认public abstract
- 接口不能被实例化,但是能通过实现类实例化,接口的子类要重写接口中的所有抽象方法,要么子类也是抽象类。
- 类与类只能单继承,可以多层继承
- 类与接口,可以但是现在,也可以多实现,还可以继承一个类的同时实现多个接口
- 接口与接口,可以单继承,也可以多继承
接口的组成 - 常量 public static final
- 抽象方法 public abstract
- 默认方法
//默认方法不是抽象方法,不强制重写,但是可以被重写,重写的时候去掉default关键字,如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写 public default void show(){ } - 静态方法
public static void show(){}
静态方法只能通过接口名调用,不能通过实现类或者对象名调用 - 私有方法
接口中私有方法是为了让默认方法和静态方法使用的,默认方法和静态方法都有方法体,其中相同的代码可以提取出来用私有方法表示。
默认方法可以调用私有的静态方法和非静态方法,静态方法只能调用私有的静态方法。
多态
同一个对象,在不同时刻表现出不同形态,
多态的前提是:
- 要有继承和实现关系,
- 要有方法的重写,
- 要有父类引用指向子类对象。
多态中成员访问
成员变量编译看父类,运行看父类
成员方法编译看父类,运行看子类
多态优点和弊端 - 优点
提高程序的扩展性,定义方法时使用父类型作为参数,使用时使用具体类型的子类型参与操作 - 不同使用子类的特有成员
多态转型
向上转型 - 父类引用指向子类对象
向下转型 - 子类型 对象名 = (子类型)父类引用
多态中转型存在风险和解决方案
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
此时需要使用instanof关键字进行判断 变量名(实际类型引用)instanof 目标类型
dog instanceof animal
内部类
在一个类中定义一个类
内部类可以直接访问外部类的成员,包括私有。外部类要访问内部类的成员时要创建对象才能访问。
成员内部类
和变量、方法一个层级。
点击查看代码
class Outer {
private int num = 10;
private class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
public class InnerDemo {
public static void main(String[] args) {
//Outer.Inner oi = new Outer().new Inner();
//oi.show();
Outer o = new Outer();
o.method();
}
}
new 类名(){重写方法}
new 接口名(){重写方法}
- 匿名内部类的本质是继承了该类或者实现了该接口的子类匿名对象
- 在开发中当发现某个方法需要接口或者抽象类的子类对象,我们就可以传递一个匿名内部类过去,用于简化代码
Lambda表达式
(形式参数)->{代码块}
形式参数:如果有多个参数,参数之间用逗号隔开,没有参数留空;
->:固定写法,代表指向动作
代码块:具体方法
- lambda表达式的使用前提是有一个接口,且这个接口中仅有一个抽象方法。
Lambda表达式和匿名内部类的区别
- 所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口 - 使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式 - 实现原理不同
匿名内部类:编译之后,产生一个单独的.class文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码文件会在运行的时候动态生成