理解面向对象:
面向对象是相对于面向过程而言。比如:用手机发送短信,相对于面向过程而言强调的是功能 :打开手机-->编写短信-->发送短信。而Java面向对象的思想就是强调具有功能的对象:手机打开,手机编写短信,手机发送短信。感觉这些过程是你自己亲自在做,而用面向对象的思却是你指挥着手机去做。面向对象和面向过程都是一种思想,面向对象是基于面向过程的。
- 面向对象是相对面向过程而言。
- 面向对象和面向过程都是一种思想。
- 面向过程:强调的是功能行为。
- 面向对象:将功能封装进对象,强调了具备功能的对象。面向对象时基于面向过程的。
-
过程和对象在我们的程序中的体现:
- 过程其实就是函数;
- 对象是将函数等一些内容进行了封装。
- 是一种符合人们思考习惯的思想
- 可以将复杂的事情简单化
- 将程序员从执行者转换成了指挥者
- 完成需求时:
- 先要去找具有所需的功能的对象来用。
- 如果该对象不存在,那么创建一个具有所需功能的对象。
- 这样简化开发并提高复用。
- 在Java中开发的过程:其实就是不断的创建对象,使用对象,指挥对象做事情。
- 设计的过程:其实就是在管理和维护对象之间的关系。
- 封装(encapsulation)
- 继承(inheritance)
- 多态(polymorphism)
1.类和对象的关系: 现实生活中的事物如:学生。想要描述:提取对象的共性内容,对具体对象的抽象。描述时:这些对象的共性有:姓名,年龄,学习java的功能。映射到java中,描述(学生)就是class定义的类。具体对象就是对应java在堆内存中用new建立的实体(凡是用于存储多个数据的,我们都称之为实体,都放在堆内存中)。对象的共性内容对应Java中类的成员。属性(姓名,年龄)对应成员变量,行为(学习java的功能)对应成员函数。也就是说:
- 使用计算机语言就是不断的在描述现实生活中的事物。
- java中描述事物通过类的形式体现。
- 类是具体事物的抽象,概念上的定义。
- 对象:对应该类事物实实在在存在的个体。
- 属性:对应的是类中的变量。
- 行为:对应的是类中的函数(方法)。
- 代码体现:
//定义学生类
Student stu = new Student();//创建一个学生对象
class Student
{
//定义属性-->成员变量
String name;
int age;
//定义功能-->成员函数
public void studyJava()
{
System.out.println("学习Java");
}
}
stu.name ="lisi"; //修改了姓名属性
stu.age = 20; //修改了年龄属性
stu.studyJava(); //调用了学习Java功能
- 其实定义类,就是在描述事物,就是在定义属性和行为。
成员变量和局部变量都是变量,只是定义位置,作用范围,内存中分配不同。
- 定义位置:
- 成员变量直接定义在类中。
- 局部变量定义在方法中,参数上,语句中。
- 作用范围:
- 成员变量在这个类中有效。
- 局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域。
- 在内存中的位置:
- 成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。
- 局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。
- 成员变量都有默认初始化值。不赋值可以参与运算。局部变量没有初始化值,不初始化不能参与运算。
- 匿名对象是对象的简化形式。
- 匿名对象使用方式
- 当对象的方法只调用一次时,可以用匿名对象来完成。
- 可以将匿名对象作为实际参数进行传递。
- 例如:Car c = new Car();c.num = 5;和new Car().num = 5; 功能相同。
- 注:如果对一个对象进行多个成员调用,必须给这个对象起个名字。
- 概念:是指隐藏对象的属性和实现细节,仅对外提供访问方式。
- 好处:
- 将变化隔离。便于使用。
- 提高重用性。提高安全性。
- 原则:将不需要对外提供的内容都隐藏起来。把属性都隐藏,提供公共方法对其访问。
- 封装的表现形式之一 --> 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 Personthis关键字
{
//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语句只能定义在构造函数的第一行。初始化动作要先执行,如果初始化中还有初始化的话,就先执行更细节的初始化。
class Personstatic(静态)
{
//定义私有属性
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特点:
- 随着类的加载而加载 。
- 当类加载到内存中时,static所修饰的成员就已经在内存中(方法区,共享区,数据区)开辟好了空间。
- 也就是说静态会随着类的消失而消失,生命周期最长。
- 优先于对象存在。静态是先存在的,对象后存在。
- 被所有对象所共享。
- 可以直接被类名所调用。 类名.静态成员。
- 随着类的加载而加载 。
- 实例变量和类变量的区别:
- 存放位置:
- 实例变量:随着对象的建立而存在于堆内存中。
- 类变量:随着类的加载而存在于方法去中。
- 生命周期:
- 实例变量:生命周期随着对象的消失而消失。
- 类变量:生命周期最长,随着类的消失而消失。
- 存放位置:
- 静态的使用注意事项:
- 静态方法只能访问静态成员(成员变量,成员函数).。非静态方法可以访问静态,也可以访问非静态。
- 静态方法中不可以定义this,super关键字。因为静态优先于对象存在,所以静态方法中不可以出现this,super关键字。
- 主函数是静态的。
- 静态有利有弊
- 利处:对对象共性数据进行单独空间存储,节省内存空间。没有必要每个对象中都存储一份,可以直接用类名调用。
- 弊端:生命周期过长,访问出现局限性,静态成员只能访问静态。
- 什么时候使用静态?
- 什么时候定义静态变量(类变量)
- 当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态存在于堆内存中。
- 什么时候定义静态函数
- 当功能内部没有访问到非静态数据(对象的特有数据),那么这时该函数(功能)可以定义成静态。
- 什么时候定义静态变量(类变量)
- 静态代码块
就是一个有静态关键字标示的一个代码块区域。定义在类中。
- 格式:
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);该句话都做了什么事情?
- 因为new用到了Person.class.所以会先找到Person.class文件并加载到内存中。
- 执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
- 在堆内存中开辟空间,分配内存地址。
- 在堆内存中建立对象的特有属性。并进行默认初始化。
- 对属性进行显示初始化。
- 对对象进行构造代码块初始化。
- 对对象进行对应的构造函数初始化。
- 将内存地址付给栈内存中的p变量。
- 静态的应用及说明书的制作
每个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。但是,很遗憾,该类中到底定义了多少个方法,对方却不清楚。因为该类并没有使用说明书。java的说明书通过文档注释来完成。
java的说明书通过文档注释来完成。 /** 类的描述信息: 包括类的功能,作者,版本等 @author 作者 @version 版本 @param 参数 @return 返回结果 */ 在dos命令行下 用javadoc命令来创建帮助文档。 javadoc -d myhelp -author -version .java文件 -d: 目录 myhelp:当前目录下如果没有myhelp文件夹,系统会自动创建 -author:作者 -version:版本示例:
/**Java中的javadoc工具就会帮我们完成说明文档也称API的制作。
这是一个可以对数组进行操作的工具类,该类中提供了,获取最值,排序等功能。
@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中有23种设计模式,单例设计模式是设计模式中的一种。
- 解决的问题:保证一个类在内存中的对象唯一性。
- 比如:多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。
- Runtime()方法就是单例设计模式进行设计的。
- 思想:
- 为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象
- 为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
- 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
- 步骤:
- 因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象;
- 在类中创建一个本类的对象;
- 定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象。(作用:可控)
- 代码体现:
- 私有化构造函数;
- 创建私有并静态的本类对象;
- 定义公有并静态的方法,返回该对象。
- 对于事物该怎么描述,还怎么描述。当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。
饿汉式。 先初始化对象。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的一种。
- 如何使用一个继承体系中的功能
- 想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系*性功能。
- 通过了解共性功能,就可以知道该体系的基本功能。那么这个体系已经可以基本使用了。
- 在具体调用时,要创建最子类的对象,为什么呢?
- 因为有可能父类不能创建对象,
- 创建子类对象可以使用更多的功能,包括基本的也包括特有的。
- 简单一句话:查阅父类功能,创建子类对象使用功能。
- 成员变量。
- 当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。
- 如果想要调用父类中的属性值,需要使用一个关键字:super
- this:代表是本类类型的对象引用。
- super:代表是子类所属的父类中的内存空间引用。
- 注意:子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
- 成员函数。
- 当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是函数的另一个特性:覆盖(复写,重写)
- 什么时候使用覆盖呢?当一个类的功能内容需要修改时,可以通过覆盖来实现。
- 构造函数。
- 发现子类构造函数运行时,先运行了父类的构造函数。
- 原因:子类的所有构造函数中的第一行,其实都有一条隐身的语句super();super(): 表示父类的构造函数,并会调用于参数相对应的父类中的构造函数。而super():是在调用父类中空参数的构造函数。
- 为什么子类对象初始化时,都需要调用父类中的函数?(为什么要在子类构造函数的第一行加入这个super()?)
- 因为:子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程。
- 注意:
- 子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super();
- 如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。
- 如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数。
- 问题:
- super()和this()是否可以同时出现的构造函数中。两个语句只能有一个定义在第一行,所以只能出现其中一个。
- super()或者this():为什么一定要定义在第一行?因为super()或者this()都是调用构造函数,构造函数用于初始化,所以初始化的动作要先完成。
- 继承的细节:
- 什么时候使用继承呢?
- 当类与类之间存在着所属关系时,才具备了继承的前提。a是b中的一种。a继承b。狼是犬科中的一种。
- 英文书中,所属关系:" is a "
- 注意:不要仅仅为了获取其他类中的已有成员进行继承。
- 所以判断所属关系,可以简单看,如果继承后,被继承的类中的功能,都可以被该子类所具备,那么继承成立。如果不是,不可以继承。
- 什么时候使用继承呢?
-
细节二:在方法覆盖时,注意两点:
- 子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。
- 覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖)
- 继承的一个弊端:打破了封装性。对于一些类,或者类中功能,是需要被继承,或者复写的。这时如何解决问题呢?介绍一个关键字,final:最终。
- final的特点
- 作为一个修饰符,可以修饰类,函数,变量。
- 被final修饰的类不可以被继承。为了避免被继承,被子类复写功能。
- 被final修饰的方法不可以被复写。
- 被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。
- 内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。
抽象类: abstract
- 抽象:不具体,看不明白。抽象类表象体现。
- 在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。
- 抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
- 抽象类的特点:
- 抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
- 抽象方法只定义方法声明,并不定义方法实现。
- 抽象类不可以被创建对象(实例化)。
- 只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
- 抽象类的细节:
- 抽象类中是否有构造函数?有,用于给子类对象进行初始化。
- 抽象类中是否可以定义非抽象方法?可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
- 抽象关键字abstract和哪些不可以共存?final , private , static
- 抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
解决的问题:当功能内部一部分实现时确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。接口 Interfaceabstract 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。
- 接口中包含的成员,最常见的有全局常量、抽象方法。
- 注意:接口中的成员都有固定的修饰符。
- 成员变量:public static final
- 成员方法:public abstract
interface Inter{
public static final int x = 3;
public abstract void show();
}
- 接口中有抽象方法,说明接口不可以实例化。接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
- 类与类之间存在着继承关系,类与接口中间存在的是实现关系。
- 继承用extends ;
- 实现用implements ;
- 接口和类不一样的地方,就是,接口可以被多实现,这就是多继承改良后的结果。java将多继承机制通过多现实来体现。
- 一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性。还可以将类进行功能的扩展。
- 其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口。
接口都用于设计上,设计上的特点:(可以理解主板上提供的接口)
1:接口是对外提供的规则。 2:接口是功能的扩展。 3:接口的出现降低了耦合性。
抽象类与接口:
- 抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取,特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。
- 接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。
- 抽象类和接口的共性:都是不断向上抽取的结果。
- 抽象类和接口的区别:
1:抽象类只能被继承,而且只能单继承。 接口需要被实现,而且可以多实现。 2:抽象类中可以定义非抽象方法,子类可以直接继承使用。 接口中都有抽象方法,需要子类去实现。 3:抽象类使用的是 is a 关系。 接口使用的 like a 关系。 4:抽象类的成员修饰符可以自定义。 接口中的成员修饰符是固定的。全都是public的。
在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。
内部类
- 定义:将一个类定义在另一个类的里面,对里面那个类就称为内部类(内之类,嵌套类)。
- 内部类的访问规则:
- 内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用。该引用写法为,外部类名.this。
- 外部类要访问内部类,必须建立内部类对象。
- 访问格式:
- 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,可以直接建立内部类对象。
- 格式:
- 外部类名.内部类名 变量名 = 外部类对象.内部类对象。
- Outer.Inner in = new Outer().new Inner();
- 当内部类在成员位置上,就可以被成员修饰符所修饰。比如:private :将内部类在外部类中进行封装。 static:内部类就具备了static的特性。
- 当内部类被static修饰后,只能直接访问外部类中的静态成员,出现了访问局限。
- 在外部其他类中,如何直接访问静态内部类的非静态成员呢?
- new Outer.Inner().function();
- 在外部其他类中,如何直接访问静态内部类的静态成员呢?
- Outer.Inner.function();
- 注意:当内部类中定义了静态成员,该内部类必须是static的。
- 当外部类中的静态方法访问内部类时,内部类也必须是静态的。
- 格式:
- 内部类定义在局部时:
- 不可以被成员修饰符修饰。
- 可以直接访问外部类中的成员,因为还持有外部类中的引用。
- 不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
- 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,可以直接建立内部类对象。
- 什么时候定义内部类
- 当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物在使用外部事物的内容。
- 匿名内部类
- 匿名内部类其实就是内部类的简写格式。
- 定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
- 匿名内部类的格式:new 父类或者接口(){定义子类的内容}
- 其实匿名内部类就是一个匿名子类对象。可以理解为带内容的对象。
- 匿名内部类中定义的方法最好不要超过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学习型技术博客、期待与您交流! ----------