面向对象编程三大特性


  • 面向对象:以对象为中心的编程思想,通过指挥对象实现具体功能。
  • 面对过程:以过程为中心的编程思想,实现具体功能都是自己实现。

类和对象的关系:

类:

  • 类是对现实生活类中具有共同属性和行为的实物的抽象。
    
  • 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合。
    
  • 类就是对现实事务的一种描述。
    

而对象则是以类为模板所创建的。

类的组成

  • *属性:指事物的特征,也就是成员变量。
    
  • *行为:指事物能执行的操作,也就是成员方法。
    
  • *构造方法:
             方法名与类名一致,没有返回值类型,没有返回值。用于创建对象,没创建一次对象,执行一次构造方法,不能手动调用构造方法。  系统默认为无参构造,如果定义了构造方法,无参构造不存在。
    

对象创建内存模型
(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字节码文件,对应的字节码文件会在运行的时候动态生成