黑马程序员——Java基础---面向对象(封装、继承、单例、抽象类、接口、内部类)

时间:2022-06-20 00:47:49
------- android培训java培训、java学习型技术博客、期待与您交流! ---------- 概述        面向对象(Object Oriented,OO)是当前计算机界关心的重点,它是90年代软件开发方法的主流。面向对象的概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
理解面向对象:
       面向对象是相对于面向过程而言。比如:用手机发送短信,相对于面向过程而言强调的是功能 :打开手机-->编写短信-->发送短信。Java面向对象的思想就是强调具有功能的对象:手机打开,手机编写短信,手机发送短信。感觉这些过程是你自己亲自在做,而用面向对象的思却是你指挥着手机去做。面向对象和面向过程都是一种思想,面向对象是基于面向过程的。
  • 面向对象是相对面向过程而言。
  • 面向对象和面向过程都是一种思想。
  • 面向过程:强调的是功能行为。
  • 面向对象:将功能封装进对象,强调了具备功能的对象。面向对象时基于面向过程的。
  • 过程和对象在我们的程序中的体现:
    • 过程其实就是函数;
    • 对象是将函数等一些内容进行了封装。
面向对象的特点:
  1. 是一种符合人们思考习惯的思想
  2. 可以将复杂的事情简单化
  3. 将程序员从执行者转换成了指挥者
  4. 完成需求时: 
    • 先要去找具有所需的功能的对象来用。
    • 如果该对象不存在,那么创建一个具有所需功能的对象。
    • 这样简化开发并提高复用。
  • 在Java中开发的过程:其实就是不断的创建对象,使用对象,指挥对象做事情。
  • 设计的过程:其实就是在管理和维护对象之间的关系。
面向对象的特征:
  1. 封装(encapsulation)
  2. 继承(inheritance)
  3. 多态(polymorphism)
一、类和对象
1.类和对象的关系:
        现实生活中的事物如:学生。想要描述:提取对象的共性内容,对具体对象的抽象。描述时:这些对象的共性有:姓名,年龄,学习java的功能。映射到java中,描述(学生)就是class定义的类。具体对象就是对应java在堆内存中用new建立的实体(凡是用于存储多个数据的,我们都称之为实体,都放在堆内存中)。对象的共性内容对应Java中类的成员。属性(姓名,年龄)对应成员变量,行为(学习java的功能)对应成员函数。也就是说:
  • 使用计算机语言就是不断的在描述现实生活中的事物。
  • java中描述事物通过类的形式体现。
    • 类是具体事物的抽象,概念上的定义。
    • 对象:对应该类事物实实在在存在的个体。
    • 属性:对应的是类中的变量。
    • 行为:对应的是类中的函数(方法)。
    • 代码体现:
      //定义学生类
      class Student
      {
      //定义属性-->成员变量
      String name;
      int age;
      //定义功能-->成员函数
      public void studyJava()
      {
      System.out.println("学习Java");
      }
      }
      Student stu = new Student();//创建一个学生对象
      stu.name ="lisi"; //修改了姓名属性
      stu.age = 20;     //修改了年龄属性    
      stu.studyJava();  //调用了学习Java功能
  • 其实定义类,就是在描述事物,就是在定义属性和行为。
2.成员变量和局部变量
成员变量和局部变量都是变量,只是定义位置,作用范围,内存中分配不同。
  • 定义位置:
    • 成员变量直接定义在类中。
    • 局部变量定义在方法中,参数上,语句中。
  • 作用范围:
    • 成员变量在这个类中有效。
    • 局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域。
  • 在内存中的位置:
    • 成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。
    • 局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。
  • 成员变量都有默认初始化值。不赋值可以参与运算。局部变量没有初始化值,不初始化不能参与运算。
3. 匿名对象
  • 匿名对象是对象的简化形式。
  • 匿名对象使用方式
    1. 当对象的方法只调用一次时,可以用匿名对象来完成。
    2. 可以将匿名对象作为实际参数进行传递。
  • 例如:Car c = new Car();c.num = 5;和new Car().num = 5; 功能相同。
  • 注:如果对一个对象进行多个成员调用,必须给这个对象起个名字。
二、封装(Encapsulation)
  • 概念:是指隐藏对象的属性和实现细节,仅对外提供访问方式。
  • 好处:
    • 将变化隔离。便于使用。
    • 提高重用性。提高安全性。
  • 原则:将不需要对外提供的内容都隐藏起来。把属性都隐藏,提供公共方法对其访问。
  • 封装的表现形式之一  --> private:私有,权限修饰符,用于修饰类中的成员(成员函数,成员变量)。私有只在本类中有效。
    • 私有只是封装的一种表现形式。之所以对外提供访问方式,就是因为可以在访问方式中加入逻辑判断等语句。对访问的数据进行操作,提高代码的健壮性。
    • 一个成员变量 通常会对应两个访问方式一个是设置值,一个是获取值。
例如:
private int age;
public void setAge(int age)//设置值
{
age = age;
}
public int getAge()//获取值
{
return age;
}

构造函数
  • 定义格式:
class 类名称
{
访问权限 类名称(类型1 参数1,类型2 参数2,…)
{
程序语句;
…//构造方法没有返回值
}
}
  • 特点:
    • 1 函数名与类名相同。
    • 2 不用定义返回值类型。
    • 3 不可以写return语句。
  • 作用:
    • 给对象进行初始化。
  • 注意:
    •  默认构造函数的特点。
      • 当一个类中没有定义构造函数时,那么系统会给该类加入一个空参数的构造函数。
      • 当在类中自定义了构造函数之后,默认的构造函数就没有了
    •  多个构造函数式以重载的形式存在的。
      • 构造函数是给不同的对象进行分别初始化。
  • 构造函数 对象一建立就会调用与之对应的构造函数
  • 一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致。
    • 如果类被public修饰,那么默认的构造函数也带public修饰符。
    • 如果类没有被public修饰,那么默认的构造函数,也没有public修饰。
    • 默认构造构造函数的权限是随着的类的变化而变化的。
  • 构造函数与一般函数不同之处:
    • 构造函数和一般函数在写法上有不同。
    • 在运行上也有不同:构造函数是在对象一建立就运行。给对象初始化。而一般方法是对象调用才执行,是给对象添加对象具备的功能。
    • 一个对象建立,构造函数只运行一次。而一般方法可以被对象调用多次。
  • 什么时候定义构造函数
    • 当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。
  • 构造函数可以被私有化,但是被私有化的构造函数式不能创建对象的。
构造代码块
  • 格式:{ 程序代码;}
  • 作用:给对象进行初始化。
  • 特点:对象一建立就运行,而且优先于构造函数执行。
  • 和构造函数的区别:
    • 构造代码块:是给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块。只要对象一建立。就会调用这个代码块。
    • 构造函数:是给与之对应的对象进行初始化。它具有针对性。
构造函数,构造代码块 示例:
class Person
{
//Perspm(){} 默认的空参数的构造函数,当在类中自定义了构造函数之后,默认的构造函数就没有了。
private String name;
private int age;
public void setName(String n)
{
name = n;
}
public String getName()
{
return name;
}
//构造代码块
{
//System.out.println("person code run");
cry();
}
Person()
{
System.out.println("A:name = " + name + ",,age =" + age);
}
Person(String n)
{
name = n;
System.out.println("B:name = " + name + ",,age =" + age);
}
Person(String n,int a)
{
name = n;
age = a;
System.out.println("C:name = " + name + ",,age =" + age);
}
public void cry()
{
System.out.println("cry......");
}
}
//测试类
class PersonDemo
{
public static void main(String[] args)
{
//相应的对象对应相应的构造函数。
Person p = new Person();//对象一建立,就会调用与之对应的构造函数。
Person p1 = new Person("lisi");
System.out.println(p1.getName());//获取姓名
p1.setName("libusi");//修改姓名
System.out.println(p1.getName());//打印修改后的姓名
Person p2 = new Person("wangwu",0);
}
}
this关键字
  • this:代表本类的对象,代表它所在函数所属对象的引用。简单说:哪个对象在调用this所在的函数,this就代表哪个对象。
  • 当定义类中功能时,该函数内部要用到调用函数的对象时,这时用this表示这个对象。但凡本类功能内部使用到了本类对象,都用this表示。
  • 应用:
    • 用于区分同名变量
    • 用于构造函数之间进行互相调用。
  • 注:this语句只能定义在构造函数的第一行。初始化动作要先执行,如果初始化中还有初始化的话,就先执行更细节的初始化。
实例:
class Person
{
//定义私有属性
private String name;
private int age;
Person (int age)//构造函数
{
this.age = age;
}
Person(String name)
{
//name = name; //局部变量的名称和成员变量的名称相同,此时两个name都是局部的name。
this.name = name;//this 表面上是区分成员变量和局部变量。
}
Person(String name,int age)
{
this(name);//this语句,调用构造函数Person(String name)
//this.name = name;
this.age = age;
}
public void speak()
{
System.out.println("name =" + this.name + ",,age =" + this.age);
this.show();
}
public void show()
{
System.out.println(this.name);
}
/*
需求:给人定义一个用于比较年龄是否相同的功能。
*/
public boolean compare(Person p)
{
return this.age == p.age;
}
}

class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person("lisi ");
Person p1 = new Person("zhangsan");
//在一个类中成员之间完成互相调用都是对象来完成的。
p.speak();
p1.speak();
Person p2 = new Person(15);
Person p3 = new Person(20);
boolean b = p2.compare(p3);//this和p2指向了同一个地址值,也就是同一个对象
System.out.println(b);
}
}
static(静态)
  • 用法:
    1. 是一个修饰符,用于修饰成员(成员变量,成员函数)。
    2. 当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,
    3. 还可以直接被类名调用。类名.静态成员。
  • static特点:
    1. 随着类的加载而加载  。
      • 当类加载到内存中时,static所修饰的成员就已经在内存中(方法区,共享区,数据区)开辟好了空间。
      • 也就是说静态会随着类的消失而消失,生命周期最长。
    2. 优先于对象存在。静态是先存在的,对象后存在。
    3. 被所有对象所共享。
    4. 可以直接被类名所调用。 类名.静态成员。
  • 实例变量和类变量的区别:
    • 存放位置:
      • 实例变量:随着对象的建立而存在于堆内存中。
      • 类变量:随着类的加载而存在于方法去中。
    • 生命周期:
      • 实例变量:生命周期随着对象的消失而消失。
      • 类变量:生命周期最长,随着类的消失而消失。
  • 静态的使用注意事项:
    1. 静态方法只能访问静态成员(成员变量,成员函数).。非静态方法可以访问静态,也可以访问非静态。
    2. 静态方法中不可以定义this,super关键字。因为静态优先于对象存在,所以静态方法中不可以出现this,super关键字。
    3. 主函数是静态的。
  • 静态有利有弊
    • 利处:对对象共性数据进行单独空间存储,节省内存空间。没有必要每个对象中都存储一份,可以直接用类名调用。
    • 弊端:生命周期过长,访问出现局限性,静态成员只能访问静态。
  • 什么时候使用静态?
    1. 什么时候定义静态变量(类变量)
      • 当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态存在于堆内存中。
    2. 什么时候定义静态函数
      • 当功能内部没有访问到非静态数据(对象的特有数据),那么这时该函数(功能)可以定义成静态。
  •  静态代码块
    • 就是一个有静态关键字标示的一个代码块区域。定义在类中。

    •  格式:
 static
 {
静态代码块中执行语句。
 }
  • 特点:
    • 随着类的加载而执行,只执行一次,如果和主函数在同一类中,优先于主函数执行。
  • 作用:
    • 用于给类进行初始化的。
  •  实例:
    class StaticCode{
    static
    {
    System.out.prontln("a");
    }
    }

    class StaticCodeDemo
    {
    static
    {
    System.out.prontln("b");
    }
    public static void main(String[] args)
    {
    new StaticCode();
    new StaticCode();//因为类已经加载,静态代码块已经执行过,所以没有打印结果。
    System.out.prontln("over");
    }
    static
    {
    System.out.prontln("c");
    }
    }

    //打印结果 b c a over

静态代码块、构造代码块、构造函数同时存在时的执行顺序:静态代码块 --> 构造代码块 -->构造函数;

示例:
class StaticCode
{
int num = 6;
StaticCode()//构造函数,没有创建与之对应的对象,所以没有打印。
{
System.out.println("b");
}
StaticCode(int x)//构造函数,给相应的对象进行初始化,
{
System.out.println("d");
}
static//静态代码块
{
System.out.prontln("a");//给类进行初始化,并优先于主函数执行
}
//构造代码块
{
System.out.prontln("c"+this.num);
//构造代码块:给对象进行初始化,对象一建立就运行,而且优先于构造函数执行。
}
}

class StaticCodeDemo
{
public static void main(String[] args)
{
new StaticCode(5);
}
}

//打印结果:a c6 d
 对象的初始化过程 实例:
 class Person
{
private Person(){}
private String name = "hah";
private int age;
private static String country = "cn";
Person(String name,int age)
{
this.name = name;
this.age = age;
}
{
System.out.println(name+".."+age);
}
public void setName(String name)
{
this.name = name;
}

public void speak()
{
System.out.println(this.name+"..."+this.age);
}

public static void showCountry()
{
System.out.println("country="+Person.country);
Person.method();
}
public static void method()
{
System.out.println("method run");
}
}

class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person("zhangsan",20);
p.setName("lisi");
}
}
  • Person p = new Person("zhangsan",20);该句话都做了什么事情?
  1. 因为new用到了Person.class.所以会先找到Person.class文件并加载到内存中。
  2. 执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
  3. 在堆内存中开辟空间,分配内存地址。
  4. 在堆内存中建立对象的特有属性。并进行默认初始化。
  5. 对属性进行显示初始化。
  6. 对对象进行构造代码块初始化。
  7. 对对象进行对应的构造函数初始化。
  8. 将内存地址付给栈内存中的p变量。
  • 静态的应用及说明书的制作
 每个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。但是,很遗憾,该类中到底定义了多少个方法,对方却不清楚。因为该类并没有使用说明书。java的说明书通过文档注释来完成。

java的说明书通过文档注释来完成。 /** 类的描述信息: 包括类的功能,作者,版本等 @author 作者 @version 版本 @param 参数 @return 返回结果 */ 在dos命令行下 用javadoc命令来创建帮助文档。 javadoc -d myhelp -author -version .java文件  -d: 目录  myhelp:当前目录下如果没有myhelp文件夹,系统会自动创建 -author:作者 -version:版本
示例:
/**
这是一个可以对数组进行操作的工具类,该类中提供了,获取最值,排序等功能。
@author 张三
@version V1.1
*/

//javadoc -d myhelp -author -version ArrayTool.java

public class ArrayTool
{
/**
空参数构造函数。
*/
private ArrayTool(){}

/**
获取一个整形数组中的最大值。
@param arr 接收一个int类型的数组。
@return 会返回一个该数组中最大值。
*/
public static int getMax(int[] arr)
{
int max = 0;
for(int x=1; x<arr.length; x++)
{
if(arr[x]>arr[max])
max = x;
}
return arr[max];
}
/**
获取一个整形数组中的最小值。
@param arr 接收一个int类型的数组。
@return 会返回一个该数组中最小值。
*/
public static int getMin(int[] arr)
{
int min = 0;
for(int x=1; x<arr.length; x++)
{
if(arr[x]<arr[min])
min = x;
}
return arr[min];
}
/**
给int数组进行选择排序。
@param arr 接收一个int类型的数组。
*/
public static void selectSort(int[] arr)
{
for (int x=0; x<arr.length-1 ; x++ )
{
for(int y=x+1; y<arr.length; y++)
{
if(arr[x]>arr[y])
{
swap(arr,x,y);
}
}
}
}
/**
给int数组进行冒泡排序。
@param arr 接收一个int类型的数组。
*/
public static void bubbleSort(int[] arr)
{
for (int x=0; x<arr.length-1 ; x++ )
{
for(int y=0; y<arr.length-x-1; y++)
{
if(arr[y]>arr[y+1])
{
swap(arr,y,y+1);
}
}
}
}
/**
给数组中元素进行位置的置换。
@param arr 接收一个int类型的数组。
@param a 要置换的位置
@param b 要置换的位置
*/
private static void swap(int[] arr,int a,int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
/**
用于打印数组中的元素。打印形式是:[elemet1, element2, ...]
*/
public static void printArray(int[] arr)
{
System.out.print("[");
for(int x=0; x<arr.length; x++)
{
if(x!=arr.length-1)
System.out.print(arr[x]+", ");
else
System.out.println(arr[x]+"]");
}
}
}
class ArrayToolDemo
{
public static void main(String[] args)
{
int[] arr = {3,1,87,32,8};
/*
ArrayTool tool = new ArrayTool();
int max = tool.getMax(arr);

虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。
发现了问题:
1,对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。
2,操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

这时就考虑,让程序更严谨,是不需要对象的。
可以将ArrayTool中的方法都定义成static的。直接通过类名调用即可。

将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。
为了更为严谨,强制让该类不能建立对象。
可以通过将构造函数私有化完成。
*/
int max = ArrayTool.getMax(arr);
int min = ArrayTool.getMin(arr);
System.out.println("max="+max);
ArrayTool.printArray(arr);
ArrayTool.bubbleSort(arr);
ArrayTool.printArray(arr);
ArrayTool.selectSort(arr);
ArrayTool.printArray(arr);
}
}
Java中的javadoc工具就会帮我们完成说明文档也称API的制作。
黑马程序员——Java基础---面向对象(封装、继承、单例、抽象类、接口、内部类)
黑马程序员——Java基础---面向对象(封装、继承、单例、抽象类、接口、内部类)


三、单列设计模式

  • 设计模式:解决问题最行之有效的思想。
    • 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
    • java中有23种设计模式,单例设计模式是设计模式中的一种。
  • 解决的问题:保证一个类在内存中的对象唯一性。
    • 比如:多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。
    • Runtime()方法就是单例设计模式进行设计的。
  • 思想:
    • 为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象
    • 为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
    • 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
  • 步骤:
    1. 因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象;
    2. 在类中创建一个本类的对象;
    3. 定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象。(作用:可控)
  • 代码体现:
    1. 私有化构造函数;
    2. 创建私有并静态的本类对象;
    3. 定义公有并静态的方法,返回该对象。
    • 对于事物该怎么描述,还怎么描述。当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。

饿汉式。 先初始化对象。Single类一进内存,就已经创建好了对象。 class Single {
private static Single s = new Single();//创建私有并静态的本类对象;
private Single(){}//私有化构造函数;
public static Single getInstance()//定义公有并静态的方法,返回该对象
{
return s;
}
}
懒汉式 对象是方法被调用时,才初始化,也叫做对象的延时加载。 class Single {
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
s = new Single();
}
return s;
}
}

四、继承(Inheritance)
       继承是面向对象的特征。当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过 extends 关键字让类与类之间产生继承关系。如:class Student extendsPerson{}
  • 继承的好处:
    • 继承的出现提高了代码的复用性。
    • 继承的出现让类与类之间产生了关系,提供了多态的前提。
  • Java只支持单继承,不支持多继承。
    • 一个类只能有一个父类,不可以有多个父类。
      • 因为多继承容易带来安全隐患:当多个父类中定义了相同的功能,当功能内容部同时,子类对象不确定要运行哪一个。
      • 但java保留了这一机制。并用另外一种体现形式来完成表现,即多实现。
    • class SubDemo extends Demo{} //ok
    • class SubDemo extends Demo1,Demo2...//error
  • Java支持多层继承,也就是一个继承体系。
    • class A{}
    • class B extends A{}
    • class C extends B{}
  • 定义继承需要注意:
    • 不要仅为了获取其他类中某个功能而去继承
    • 类与类之间要有所属( " is a " )关系,xx1是xx2的一种。
  • 如何使用一个继承体系中的功能
    • 想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系*性功能。
    • 通过了解共性功能,就可以知道该体系的基本功能。那么这个体系已经可以基本使用了。
    • 在具体调用时,要创建最子类的对象,为什么呢?
    1. 因为有可能父类不能创建对象,
    2. 创建子类对象可以使用更多的功能,包括基本的也包括特有的。
    • 简单一句话:查阅父类功能,创建子类对象使用功能。
子父类出现后,类成员的特点:
  1. 成员变量。
    • 当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。
    • 如果想要调用父类中的属性值,需要使用一个关键字:super 
    • this:代表是本类类型的对象引用。
    • super:代表是子类所属的父类中的内存空间引用。
    • 注意:子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
  2. 成员函数。
    • 当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是函数的另一个特性:覆盖(复写,重写)
    • 什么时候使用覆盖呢?当一个类的功能内容需要修改时,可以通过覆盖来实现。
  3. 构造函数。
    • 发现子类构造函数运行时,先运行了父类的构造函数。
    • 原因:子类的所有构造函数中的第一行,其实都有一条隐身的语句super();super(): 表示父类的构造函数,并会调用于参数相对应的父类中的构造函数。而super():是在调用父类中空参数的构造函数。
    • 为什么子类对象初始化时,都需要调用父类中的函数?(为什么要在子类构造函数的第一行加入这个super()?)
    • 因为:子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程。
  • 注意:
    • 子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super(); 
    • 如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。
    • 如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数。
  • 问题:
    • super()和this()是否可以同时出现的构造函数中。两个语句只能有一个定义在第一行,所以只能出现其中一个。
    • super()或者this():为什么一定要定义在第一行?因为super()或者this()都是调用构造函数,构造函数用于初始化,所以初始化的动作要先完成。
  • 继承的细节:
    • 什么时候使用继承呢?
      • 当类与类之间存在着所属关系时,才具备了继承的前提。a是b中的一种。a继承b。狼是犬科中的一种。
      • 英文书中,所属关系:" is a "
      • 注意:不要仅仅为了获取其他类中的已有成员进行继承。
      • 所以判断所属关系,可以简单看,如果继承后,被继承的类中的功能,都可以被该子类所具备,那么继承成立。如果不是,不可以继承。
    • 细节二:在方法覆盖时,注意两点:
      1. 子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。
      2. 覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖)
  • 继承的一个弊端:打破了封装性。对于一些类,或者类中功能,是需要被继承,或者复写的。这时如何解决问题呢?介绍一个关键字,final:最终。
 final关键字
  • final的特点
    1. 作为一个修饰符,可以修饰类,函数,变量。
    2. 被final修饰的类不可以被继承。为了避免被继承,被子类复写功能。
    3. 被final修饰的方法不可以被复写。
    4. 被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。
    5. 内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。
        当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成。单词间通过_连接。
抽象类: abstract
  • 抽象:不具体,看不明白。抽象类表象体现。
  • 在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。
  • 抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
  • 抽象类的特点:
    1. 抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
    2. 抽象方法只定义方法声明,并不定义方法实现。
    3. 抽象类不可以被创建对象(实例化)。
    4. 只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
  • 抽象类的细节:
    1. 抽象类中是否有构造函数?有,用于给子类对象进行初始化。
    2. 抽象类中是否可以定义非抽象方法?可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
    3. 抽象关键字abstract和哪些不可以共存?final , private , static 
    4. 抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
模板方法设计模式:
解决的问题:当功能内部一部分实现时确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
abstract class GetTime{
public final void getTime(){ //此功能如果不需要复写,可加final限定
long start = System.currentTimeMillis();
code(); //不确定的功能部分,提取出来,通过抽象方法实现
long end = System.currentTimeMillis();
System.out.println("毫秒是:"+(end-start));
}
public abstract void code(); //抽象不确定的功能,让子类复写实现
}
class SubDemo extends GetTime{
public void code(){ //子类复写功能方法
for(int y=0; y<1000; y++){
System.out.println("y");
}
}
}

接口 Interface
  1. 接口:初期理解,可以认为是一个特殊的抽象类。当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
  2. 定义接口的关键字:interface。
  3. 接口中包含的成员,最常见的有全局常量、抽象方法。
    • 注意:接口中的成员都有固定的修饰符。
    • 成员变量:public static final 
    • 成员方法:public abstract 
      interface Inter{
      public static final int x = 3;
      public abstract void show();
      }
  4. 接口中有抽象方法,说明接口不可以实例化。接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
  5. 类与类之间存在着继承关系,类与接口中间存在的是实现关系。
    • 继承用extends  ;
    • 实现用implements ;
  6. 接口和类不一样的地方,就是,接口可以被多实现,这就是多继承改良后的结果。java将多继承机制通过多现实来体现。 
  7. 一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性。还可以将类进行功能的扩展。
  8. 其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口。
接口都用于设计上,设计上的特点:(可以理解主板上提供的接口)
1:接口是对外提供的规则。 2:接口是功能的扩展。 3:接口的出现降低了耦合性。

抽象类与接口:
  • 抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取,特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。
  • 接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。
  • 抽象类和接口的共性:都是不断向上抽取的结果。
  • 抽象类和接口的区别:
1:抽象类只能被继承,而且只能单继承。      接口需要被实现,而且可以多实现。  2:抽象类中可以定义非抽象方法,子类可以直接继承使用。      接口中都有抽象方法,需要子类去实现。 3:抽象类使用的是  is a 关系。       接口使用的 like a 关系。  4:抽象类的成员修饰符可以自定义。      接口中的成员修饰符是固定的。全都是public的。
在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。

内部类
  1. 定义:将一个类定义在另一个类的里面,对里面那个类就称为内部类(内之类,嵌套类)。
  2. 内部类的访问规则:
    1. 内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用。该引用写法为,外部类名.this。
    2. 外部类要访问内部类,必须建立内部类对象。
  3. 访问格式:
    1. 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,可以直接建立内部类对象。
      1. 格式:
        1. 外部类名.内部类名 变量名 = 外部类对象.内部类对象。
        2. Outer.Inner in = new Outer().new Inner();
      2. 当内部类在成员位置上,就可以被成员修饰符所修饰。比如:private :将内部类在外部类中进行封装。 static:内部类就具备了static的特性。
        1. 当内部类被static修饰后,只能直接访问外部类中的静态成员,出现了访问局限。
        2. 在外部其他类中,如何直接访问静态内部类的非静态成员呢?
          • new Outer.Inner().function();
        3. 在外部其他类中,如何直接访问静态内部类的静态成员呢?
          • Outer.Inner.function();
          • 注意:当内部类中定义了静态成员,该内部类必须是static的。
          • 当外部类中的静态方法访问内部类时,内部类也必须是静态的。 
    2. 内部类定义在局部时:
      1. 不可以被成员修饰符修饰。
      2. 可以直接访问外部类中的成员,因为还持有外部类中的引用。
      3. 不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
  4. 什么时候定义内部类
    • 当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物在使用外部事物的内容。
  5. 匿名内部类
    1. 匿名内部类其实就是内部类的简写格式。
    2. 定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
    3. 匿名内部类的格式:new 父类或者接口(){定义子类的内容}
    4. 其实匿名内部类就是一个匿名子类对象。可以理解为带内容的对象。
    5. 匿名内部类中定义的方法最好不要超过3个。 
实例:
abstract class AbsDemo
{
abstract void show();
}
class Outer
{
private static int x = 8;
static class Inner//静态内部类
{
public static void show()
{
System.out.println("inner show :" + x);
}
public void function()
{
show();
AbsDemo d = new AbsDemo()//匿名内部类
{
final int y = 7;
void show()
{
System.out.println("匿名内部类:y="+y);
}
void abc()
{
System.out.println("输出:");
}
};
d.show();
//d.abc();编译失败
new Inner2().show();
}
}

static class Inner2//静态内部类
{
void show()
{
System.out.println("inner2 show");

}
}
class Inner3//内部类
{
int x = 4;
void function()
{
int x = 6;
System.out.println("inner3:"+x); //6
System.out.println("inner3:"+this.x); //4
System.out.println("inner3:"+Outer.this.x);//8
}

public void method()
{
Inner in = new Inner();
in.function();
}
}
}
public class InnerClassDemo5 {
public static void main(String[] args) {
new Outer.Inner().function();
Outer.Inner3 in = new Outer().new Inner3();
in.function();
}
}

------- android培训java培训、java学习型技术博客、期待与您交流! ----------