Java面向对象

关于Java面向对象的基础内容。


面向对象和面向过程

首先Java是面向对象的一门语言,最大的特点在于平台无关性。

面向过程

优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源。比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展。

面向对象

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,是系统更加灵活、更加易于维护。
缺点:性能比面向过程低。

面向对象的三大特性

封装

封装是把一个对象的属性私有化,外部只能通过对象的方法与对象数据进行交互。

继承

继承是指可以让某个类型的对象获得另一个类型对象的(全部)属性和方法(非private),在此基础上还可以添加自己的方法和属性,来对父类进行扩展,还可以重写父类的方法。

继承要注意的点:

  1. 父类的私有的成员变量不能被继承(子类会拥有,但不能使用,除非用父类的public或protected方法调用)。
  2. 父类的构造方法也不能被继承
  3. 创建子类对象时默认会先调用父类的无参的构造函数。(为了初始化从父类继承下来的成员变量)。

多态

多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

static关键字

static特点

  1. 随着类的加载而加载,静态会随着类的加载而加载,随着类的消失而消失。说明它的生命周期很长。
  2. 优先于对象存在。–>静态是先存在,对象是后存在。
  3. 被所有实例(对象)所共享。
  4. 可以直接被类名调用

static使用场景

  1. 修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:类名.静态变量名 类名.静态方法名()
  2. 静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
  3. 静态内部类(static修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:

    1. 它的创建是不需要依赖外围类的创建。
    2. 它不能使用任何外围类的非static成员变量和方法。
      静态内部类实现单例模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class Singleton {

      声明为 private 避免调用默认构造方法创建对象
      private Singleton() {
      }

      声明为 private 表明静态内部该类只能在该 Singleton 类中被访问
      private static class SingletonHolder {
      private static final Singleton INSTANCE = new Singleton();
      }

      public static Singleton getUniqueInstance() {
      return SingletonHolder.INSTANCE;
      }
      }

      当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
      这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

  4. 静态导包(用来导入类中的静态资源,1.5之后的新特性): 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。

static的优缺点

优点:对对象的共享数据进行单独空间的存储,节省空间
缺点:生命周期过长且访问出现局限性。(静态只能访问静态)

构造代码块

构造代码块是在每次创建对象时执行。如下几个细节

  1. 构造函数的代码是位于成员变量的显式初始化语句、构造代码块语句之后执行的,即构造函数里的代码是最后执行
  2. 成员变量的显式初始化语句与构造代码块的语句的先后执行顺序是按照当前代码的顺序执行的。
  3. 成员变量的初始化语句和构造代码块的代码其实是在构造函数中完成的。

类的执行顺序

对于一个类而言:

  1. 执行静态代码块
  2. 执行构造代码块
  3. 执行构造函数

对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)> 构造器。

静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new一次就执行一次。 非静态代码块可在普通方法中定义(不过作用不大);而静态代码块不行。

非静态代码块与构造函数的区别是: 非静态代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。

当涉及到继承时,按照如下顺序执行:

  1. 执行父类的静态代码块,并初始化父类静态成员变量
  2. 执行子类的静态代码块,并初始化子类静态成员变量
  3. 执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量
  4. 执行子类的构造代码块, 执行子类的构造函数,并初始化子类普通成员变量

final关键字

哪里使用

主要用于修饰类、类成员、方法、以及方法的形参

  1. 成员变量:表示该成员变量是常量,不能被修改,常量名要大写,且需要赋值。
  2. 类:表示该类不能被继承,可以防止代码功能被重写,不再进行功能扩展。
  3. 方法:表示该方法是最终方法,不能被重写。
  4. 形参:当形参被修饰为final,那么该形参所属的方法中不能被修改。

关键字super

在子类调用父类的属性、方法、构造函数时可以使用。在子类的构造函数中会隐式的调用,当子类自动调用时,它调用的时父类的无参构造函数,我们也可以显式调用,调用有参父类构造函数。
值得注意的一点是,super和this不能同时存在,他们都必须放在构造函数的第一句。

内部类

内部类: 在一个类的内部定义另外一个类,那么另外一个类则称作为内部类 。

内部类的class文件名: 外部类$内部类.class

成员内部类:

成员内部类的访问方式:

方式一:在外部类提供一个方法创建内部类的对象进行访问。

方式二:在其他类中直接创建内部的对象进行访问。

格式: 外部类.内部类  变量名  = new 外部类().new 内部类();

当静态成员内部类在其他类创建对象的格式:

外部类.内部类  变量名  =  new 外部类.内部类();

内部类好处: 直接访问外部类的所有成员。

细节:

1. 内部类可以直接访问外部类的所有成员。
2. 内部类与外部类存在同名的成员时, 在内部类中默认是访问内部类的成员, 可以通过"外部类.this.成员"进行指定访问外部类的成员。
3. 私有的成员内部类只能通过在外部类提供一个公共的方法进行访问。 在其他类无法访问。
4.  如果一个成员内部类定义了静态的成员,那么该类也必须使用static修饰。

局部内部类:

在一个方法内部定义的类称作为局部内部类。

细节:如果局部内部类要访问局部变量,那么局部变量必须使用final去修饰。

匿名内部类:

没有类名的内部类就称作为匿名内部类。

匿名内部类的好处: 简化书写,在jdk8中使用lambda再次简化

匿名内部类使用前提: 必须存在继承或者实现的关系。

细节:匿名内部类只是没有类名而已,其他的一切成员都是具备的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AnonymityInner {
public static void main(String[] args) {
today(new TodayWeather() {
@Override
public String getDay() {
return "星期一";
}
@Override
public String getWeather() {
return "晴";
}
});
}
public static void today(TodayWeather todayWeather){
System.out.println("今天是星期"+ todayWeather.getDay() + "天气"+ todayWeather.getWeather());
}
}
interface TodayWeather{
String getDay();
String getWeather();
}


部分内容引用

------------- 感谢阅读-------------