Java的核心思想就是OOP(Object Oriented Programming)

面向过程&面向对象

面向过程思想

  • 步骤清晰简单,第一步做什么,第二步做什么….

  • 面对过程适合处理一些较为简单的问题

面向对象思想

  • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。

  • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

什么是面向对象

面向对象编程(Object-Oriented Programming, OOP)

面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。

抽象

三大特性:

  • 封装

  • 继承

  • 多态

从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。

从代码运行角度考虑是先有类后有对象。类是对象的模板。


方法

方法的定义

  • 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。

  • 返回类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void

  • break和return的区别:break是跳出循环,return则是结束整个方法。

  • 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。命名注意规范就好。

  • 参数列表:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。

  • 异常抛出:遇到异常时需要抛出。

方法的调用

  • 静态方法:需要static修饰符,可以直接通过类名调用。

  • 非静态方法:需要先实例化后,再通过实例化的对象调用。

  • 形参和实参:形参就是方法里需要传入的参数,实参是实际传入到方法里的参数。

  • 值传递和引用传递:值传递:对形参的修改不会影响到实参。引用传递:对形参的修改能够影响到实参。Java是值传递,但是创建好的对象(实体类)是引用传递。

  • this关键字

注意:

Java中的基本的数据类型都是值传递,引用数据类型中的String也是值传递

引用数据类型除了String之外都是引用传递


类与对象的关系

类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。

  • 动物、植物、手机、电脑…..

  • Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为

对象是抽象概念的具体实例

  • 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。

  • 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。


创建与初始化对象

创建对象

使用new关键字创建对象。

创建对象需要以下三步:

  • 声明:声明一个对象,包括对象名称和对象类型。

  • 实例化:使用关键字 new 来创建一个对象。

  • 初始化:使用 new 创建对象时,会调用构造方法初始化对象。

下面是一个创建对象的例子:

public class Puppy{
   public Puppy(String name){
      //这个构造器仅有一个参数:name
      System.out.println("小狗的名字是 : " + name ); 
   }
   public static void main(String[] args){
      // 下面的语句将创建一个Puppy对象
      Puppy myPuppy = new Puppy( "tommy" );
   }
}

// 小狗的名字是 : tommy

构造器(构造方法)

使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。

类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点:

  1. 必须和类的名字相同。

  2. 必须没有返回类型,也不能写void。

  3. 如果定义了有参数的构造器并且还需要无参数构造器,请自己定义一个。

构造器必须要掌握

在创建(new)一个对象的时候,至少要调用一个构造器。

每个类都有构造器。如果没有显式地为类定义构造器,Java 编译器将会为该类提供一个默认构造器。

构造器的名称必须与类同名,一个类可以有多个构造器。

下面是一个构造方法示例:

public class Puppy{
    public Puppy(){
    }
 
    public Puppy(String name){
        // 这个构造器仅有一个参数:name
    }
}

创建对象内存分析

太麻烦了不太好解释,推荐自行阅读下面的文章。

推荐文章:

Java类加载内存分析

类加载内存分析(Java)


封装

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。

要访问该类的代码和数据,必须通过严格的接口控制。

封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。

适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

该露的露,该藏的藏

我们程序设计要追求“高内聚,低耦合”。

  • 高内聚:类的内部操作细节自己完成,不允许外部干涉;

  • 低耦合:仅暴露少量的方法给外部使用

  • 封装(数据的隐藏)

  • 通常,应禁止直接访问一个对象中数据的实际表示。而应通过操作接口来访问,这称为信息隐藏。

记住这句话就够了:属性私有,get/set。

封装的优点

  • 1. 良好的封装能够减少耦合。

  • 2. 类内部的结构可以自由修改。

  • 3. 可以对成员变量进行更精确的控制。

  • 4. 隐藏信息,实现细节。


继承

继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。

extands的意思是“扩展”。子类是父类的扩展。

JAVA中类只有单继承,没有多继承!

继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。

继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。

子类和父类之间,从意义上讲应该具有"is a"的关系。

继承类型

image-4.png

类的继承格式

class 父类 {
}
 
class 子类 extends 父类 {
}

继承的特性

  • 子类拥有父类非 private 的属性、方法。

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  • 子类可以用自己的方式实现父类的方法。

  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

object类

在IDEA中用ctrl+H可以查看继承树。

super

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

/*
输出结果
animal : eat
dog : eat
animal : eat
*/

方法重写(Override)

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,抛出 IOException 异常或者 IOException 的子类异常。

在面向对象原则里,重写意味着可以重写任何现有方法。实例如下:

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 对象
      Animal b = new Dog(); // Dog 对象
 
      a.move();// 执行 Animal 类的方法
 
      b.move();//执行 Dog 类的方法
   }
}
/*
以上实例编译运行结果如下:

动物可以移动
狗可以跑和走
*/

多态

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。

即同一方法可以根据发送对象的不同而采用多种不同的行为方式。

一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)

多态存在的条件

  • 有继承关系

  • 子类重写父类方法

  • 父类引用指向子类对象

注意:多态是方法的多态,属性没有多态性。

多态的优点

  • 1. 消除类型之间的耦合关系

  • 2. 可替换性

  • 3. 可扩充性

  • 4. 接口性

  • 5. 灵活性

  • 6. 简化性

多态存在的三个必要条件

  • 继承

  • 重写

  • 父类引用指向子类对象:Parent p = new Child();

image-5.png

多态的实现方式

  • 方式一:重写

  • 方式二:接口

    1. 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。

    2. java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。

  • 方式三:抽象类和抽象方法

instanceof

instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。

instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

类型转换

具体规则方式和基础变量类型的转换类似


static关键字详解

在java中,static是一个修饰符,用于修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。

被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。

static修饰成员方法

static修饰的方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。

但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。

提示:可以进行静态导入某函数,如;

import static java.uitl.Math.random;

static修饰成员变量

static修饰的变量也称为静态变量

静态变量和非静态变量的区别是:

  • 静态变量被所有对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。

  • 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

static成员变量的初始化顺序按照定义的顺序进行初始化。

static修饰代码块

static关键字还有一个比较重要的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来依次执行每个static块,并且只会执行一次。

static块可以优化程序性能,是因为它的特性:只会在类被初次加载的时候执行一次。


抽象类

abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。

抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。

抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。

子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

抽象类总结规定

  1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。

  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

  6. 抽象类可以有构造方法

    1. 抽象类中可以有抽象方法和普通方法,

    2. 普通方法可以有方法体,构造方法是没有返回值的方法,在new实例化对象时被调用。所以抽象类可以有构造方法。

接口

  • 普通类:只有具体实现

  • 抽象类:具体实现和规范(抽象方法)都有

  • 接口:只有规范

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人

接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。

OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

接口简介

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

接口与类相似点:

  • 一个接口可以有多个方法。

  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。

  • 接口的字节码文件保存在 .class 结尾的文件中。

  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别:

  • 接口不能用于实例化对象。

  • 接口没有构造方法。

  • 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。

  • 接口不能包含成员变量,除了 static 和 final 变量。

  • 接口不是被类继承了,而是要被类实现。

  • 接口支持多继承。

接口特性

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。

  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。

  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  • 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

  • 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

  • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。

  • 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

:JDK 1.8 以后,接口里可以有静态方法和方法体了。

:JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰。更多内容可参考 Java 8 默认方法

:JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。更多内容可参考 Java 9 私有接口方法

接口的声明

接口的声明语法格式如下:

[可见度] interface 接口名称 [extends 其他的接口名] {
        // 声明变量
        // 抽象方法
}

接口有以下特性:

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。

  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。

  • 接口中的方法都是公有的。

接口的实现

当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。

类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。

实现一个接口的语法,可以使用这个公式:

...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...

重写接口中声明的方法时,需要注意以下规则:

  • 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。

  • 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。

  • 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。

在实现接口的时候,也要注意一些规则:

  • 一个类可以同时实现多个接口。

  • 一个类只能继承一个类,但是能实现多个接口。

  • 一个接口能继承另一个接口,这和类之间的继承比较相似。

接口的继承

一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

下面的Sports接口被Hockey和Football接口继承:

public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}
 
// 文件名: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}
 
// 文件名: Hockey.java
public interface Hockey extends Sports
{
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

/*
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。

相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
*/

接口的多继承

在Java中,类的多继承是不合法,但接口允许多继承。

在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:

public interface Hockey extends Sports, Event

以上的程序片段是合法定义的子接口,与类不同的是,接口允许多继承,而 Sports及 Event 可以定义或是继承相同的方法。


内部类

内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。

  1. 成员内部类

  2. 静态内部类

  3. 局部内部类

  4. 匿名内部类

成员内部类

在类的内部方法的外部编写的类就是成员内部类。

员内部类特点:

成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。

同名属性名方法名时访问外部类 外部类.this.成员名。

MemberDemo.this.name

成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。所以在外部类访问内部类的时候必须先实例化外部类对象。

MemberDemo member = new MemberDemo();
Inner inner = member.new Inner();


成员内部类可以使用四种权限修饰符进行修饰。

成员内部类中不能书写静态变量和方法。

实例:

public class MemberDemo {
	String name = "王五";
	static int age = 10;
	
	public static void show() {
		System.out.println("掉用外部类中的show方法");
		
	}
	
	public void printf() {
		System.out.println("调用外部类中的打印方法");
	}
	
	//成员内部类 可以使用权限修饰符进行修饰
	public class Inner{
		//static double height=1.8;  成员内部类中不能使用static修饰变量和方法
		
		String name="张三";
		//成员内部类可以直接访问外部类的属性和方法
		public void innerShow() {
			show();
			printf();
			System.out.println(age);
			System.out.println("我是:"+name);
			System.out.println("我是:"+MemberDemo.this.name);//进行特指访问时 使用类名.this.变量名进行访问
		}
	}
	
	
	public static void main(String[] args) {
		//成员内部类对象的创建步骤
		//第一步需要实例化外部类对象
		//第二步正常实例化内部类对象 但是new关键字要改成 外部类对象名.new
		MemberDemo member = new MemberDemo();
		Inner ineer = member.new Inner();
		ineer.innerShow();
	} 
	
}

局部内部类

编写在方法的内部的类称之为局部内部类

局部内部类的特点

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

局部内部类不可使用权限修饰符 静态修饰符进行修饰 同局部变量相同

局部内部类可以直接访问方法中的属性

局部内部类 可以直接访问方法外部类中属性和方法

局部内部类 创建对象 要在方法内部 局部内部类的外部声明

实例:

public class PartialDemo {
		String name = "王五";
		static int age = 10;
		
		public static void show() {
			System.out.println("掉用外部类中的show方法");
			
		}
		
		public void printf() {
			System.out.println("调用外部类中的打印方法");
		}
			
		public void demo() {
			String name = "张三";
			double height = 1.8;
			//编写在方法的内部的类称之为局部内部类
			//局部内部类不可使用权限修饰符 静态修饰符进行修饰 同局部变量相同
			//局部内部类与局部变量使用范围一样 在此方法内部
			//局部内部类可以直接访问方法中的属性 重名时使用参数传递完成访问
			
			//局部内部类 可以访问方法外部类中属性和方法  
			
			 class Inner{
				  String name = "李四";				  
				  public void showInner(String name) {
					  show();
					  printf();
					  System.out.println(age);
					  System.out.println(height); 
					  System.out.println("这是:"+PartialDemo.this.name);
					  System.out.println("这是:"+name);
					  System.out.println("这是:"+this.name);
					 
				  }
			}
			 //局部内部类 创建对象 要在方法内部 局部内部类的外部声明
			 Inner inner=new Inner();
			 inner.showInner(name);
			 
		}
		
		public static void main(String[] args) {
			PartialDemo partialDemo = new PartialDemo();
			partialDemo.demo();
			
		}

	

}

匿名内部类

匿名内部类特点

匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。

一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

实例:

interface A{
	void show();
}

public class AnonymousDemo {			
			//编写回调方法 参数类型为接口A
			public void calllnner(A a) {
				a.show();
			}
			
			
			public static void main(String[] args) {
				AnonymousDemo anonymousDemo = new AnonymousDemo();
				
				//匿名内部类 监听事件使用较多
				anonymousDemo.calllnner(new A() {//接口回调
					
					//实现子类 但是没有名字 所以叫匿名内部类
					@Override
					public void show() {
						// TODO Auto-generated method stub
						System.out.println("show");
					}
					
				});
				
			}

}

或许有些难以理解 过程其实并不复杂
首先有一个接口,然后在使用的类中编写了一个方法(参数类型是接口对象),并使用接口中未实现的方法。
我们调用此方法直接构造一个接口对象传入,此时会自动生成一个此接口的子类(匿名内部类)实现接口中的方法。本质传入的类便是此时的匿名内部类。