您的位置:首页 > 编程语言 > Java开发

Java基础:面向对象

2015-04-28 15:27 183 查看
第一部分:面向对象的基础

一、面向对象的概念

理解面向对象:

1)面向对象是相对面向过程而言。

2)面向对象和面向过程都是一种思想。

3)面向过程强调的是功能行为。

4)面向对象是将功能封装进对象,强调具备功能的对象。

5)面向对象是基于面向过程的。

 

实例:将大象装进冰箱里面。

面向过程:把大象装进冰箱里。

(a)打开冰箱

(b)装入大象

(c)关闭冰箱

面向过程注重的是打开、存储与关闭的功能行为。

面向对象:

        打开、存储与关闭均是冰箱这类事物对应的行为,因为操作的都是冰箱。因此可以将这些动作定义到冰箱当中,冰箱是一个实实在在存在的东西,称之为对象。而现在看到的不再是打开、存储与关闭,而是冰箱,冰箱中定义了打开、存储与关闭的动作。

图解:





总结:

1、面向过程是一种思想。C语言就是面向过程的语言。

2、面向对象的出现将复杂的事情简单化,面向过程需要面对多个动作,而现在只需要面对一个对象即可。

        上图中左边需要从头到尾执行这些动作,而到了右边找到冰箱,让冰箱去打开、关闭和存储就可以了。角色由之前的执行者变成了指挥者(指挥对象做事情)。

面向过程是面向对象的基础,面向对象基于面向过程。

3、面向对象的思想落实到程序:

        写程序时不是先定义功能,而是先面向对象,对象需要什么功能就就该功能定义到对象中去,封装好了之后下次若要使用该功能,则只要找到该对象,功能就有了。

        开发时先找Java中是不是已经提供了相对性的对象,若有,则直接拿来使用;若没有,则再定义。

(找对象,建对象,用对象,并维护对象之间的关系)

在Java中,一切皆对象。
面向对象的三个特征:封装、继承和多态

二、类与对象的关系

类就是对现实生活中事物的描述,描述事物就是描述事物的属性和行为(属性对应类中的变量,行为对应类中的方法)。

对象就是这类事物实实在在存在的个体。

映射到Java中:

描述:就是class定义的类。

对象:就是对应Java在堆内存中通过new操作符产生的实体。(数据多了用对象存)

示例:对汽车进行描述



可以理解为类就是图纸,汽车就是堆内存中的实体。

package demo;
//对汽车这类事物进行描述
class Car{
//描述颜色
String color = "red";
//描述轮胎数
int num = 4;

//运行行为
void run(){
System.out.println("color = "+color+"\tnum = "+num);
}
}
public class CarDemo{
public static void main(String[] args){

//生产汽车。在Java中通过new操作来完成
//其实就是在堆内存中产生了一个实体/对象。
Car c = new Car();

//将已有的颜色改为蓝色(指挥对象做事情)
c.color = "blue";

c.run();
}
}


内存中的体现:



对象的特点:封装数据,数据包括属性和行为(属性与行为统称为类中的成员,成员变量、成员函数)。


4000
操作对象:
对象.对象成员

注意:

1)描述事物的类没有主函数,因为该类不需要独立运行,若想使用该类中封装的属性和功能,直接建立该类对象,随时都可以使用。

2)变量定义在了函数的外面,称之为成员变量。

成员变量与局部变量:

1)局部变量作用于函数或for循环中,并且存在于栈内存中,没有默认初始化值。

2)成员变量作用于整个类中,因为对象的存在而存在与堆内存中,有默认初始化值。(静态成员存在与方法区中)

匿名对象:

1、匿名对象是对象的简化形式。

2、匿名对象的两种使用情况。

1)对象的方法只调用一次。(若对一个对象进行多个成员调用,必须给该对象起名)

2)调用函数/功能时作为实际参数传递。

示例:

new Car().num = 5;
new Car().color = "blue";
new Car().run();


第一句代码一执行完毕就变成垃圾,因为没有被使用。第二句话同样如此。

第三局代码有意义,因为调用的是方法,方法中有方法体,可以有运行内容在里面。

因此,匿名对象调用属性无意义,调用方法有意义,有时还可以简化代码。



三、封装

1、概念

        封装,是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

2、封装的好处

1)将变化隔离。(属性如何变化和对象没关系)

2)便于使用。(不用了解内部构造)

3)提高重用性。

4)提高安全性。

函数是Java中最小的封装体。

3、关键字private

 private:权限修饰符,用于修饰类中的成员(成员变量与成员方法)。私有后成员仅在本类中有效,外界无法直接访问。

对私有的成员变量一般都会提供相对性的两个访问方式set,get。(可以对外界输入的属性进行逻辑判断)

示例代码:

package demo;

//对人进行描述
class Person{
//将age与name私有,不让外界直接访问
private int age;
private String name;

//描述speak功能
public void speak(){
System.out.println("name="+name+" age="+age);
}

//对外界提供age的设置与获取方式
public int getAge() {
return age;
}
public void setAge(int age) {
if(age<0){
System.out.println("输入错误");
return ;
}
this.age = age;
}
//对外界提供name的设置与获取方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

public class PersonDemo{
public static void main(String[] args){
Person p = new Person();
//p.age = 20;报错,私有后无法访问

//对姓名和年龄进行设置
p.setAge(24);
p.setName("zhangsan");
//调用p对象的speak功能
p.speak();
}
}


输出结果为 :



注意:

1)封装不是私有,私有仅仅是封装的一种表现形式,不私有也能实现封装。(不同包中的默认权限也是一种封装形式)

2)私有是最小权限。

四、构造函数

1、特点

1)函数名与类名相同。

2)不用定义返回值类型。(与void不一样)

3)不可以写return语句。

2、作用

给对象进行初始化。

        对象一建立就会调用与之相对应的构造函进行初始化。当一个类中没有构造函数时,系统会默认给该类加一个空参数的构造函数,方便该类进行初始化。

        当类中自定义了构造函数后,默认的构造函数就消失了。此时若要建立该类对象,则必须访问指定的构造函数以进行初始化,否则会发生错误。

构造函数与一般函数的区别:

1)写法不同。构造函数名与类名相同。

2)运行上的不同。构造函数在对象一建立时就会运行,对对象进行初始化。一般方法是对象调用才执行,是给对象添加具备的功能。

3)同一个对象,构造函数只运行一次,而一般方法可以被该对象调用多次。

什么时候使用构造函数?

        当分析事物时,该事物就具备了一些特性/行为,那么将这些内容定义在构造函数中。(如果构造函数中有未知内容参与运算,则定义参数)

注意:

        构造函数可以被私有化(private),但此后无法在外部建立与之相对应的对象;若一个类中所有的构造函数都被私有化,则该类无法在外部建立对象,因为所有的对象都不能进行初始化动作。

3、构造代码块

1)作用:给所有对象进行初始化。(对象一建立就运行,而且优先于构造函数执行)

2)与构造函数的区别:

构造代码块是给所有对象进行统一初始化的,而构造函数是给对应的对象进行初始化的。

示例:

package demo;
class Person{
private int age;
private String name;

//对象一初始化就具备name和age
Person(String name,int age){
this.name = name;
this.age = age;
}
//构造代码块,由于给所有对象进行初始化
{
System.out.println("All-run");
}

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

public class PersonDemo{
public static void main(String[] args){
//建立对象,并给构造函数传递指定参数
Person p1 = new Person("lisi",22);
p1.speak();

Person p2 = new Person("wangwu",18);
p2.speak();
}
}


结果为:



五、this关键字

1、含义

this代表它所在函数所属对象的引用。即:哪个对象在调用this所在函数,this就代表哪个对象。

类里面的成员被使用完全也是由对象完成的。(省略了this.)

2、this的应用:

        当定义类中的功能时,该函数内部要调用到该函数所属的对象时,用this来代替这个对象。但凡本类功能内部使用了本类对象,都用this来表示。

3、构造函数之间的调用

示例:

class Person{
private int age;
private String name;
Person(String name,int age
19f08
){
//调用指定的构造函数
this(name);
this.age = age;
}
Person(String name){
this.name = "张三";
}
}


注意:

1)this语句只能用在构造函数的第一行。(初始化的动作要先执行,如果初始化中还有初始化,就先执行更细节的初始化)

2)一般函数是不能直接调用构造函数的,因为this语句只能用于构造函数间的相互调用,不能用于一般函数中。

六、static关键字

1、static(静态)

        static:是一个修饰符,用于修饰类中的成员(包括成员变量、成员方法,不能修饰局部变量)。被static修饰后,成员变量就不在堆内存中了,而是被单独提取出来,存放在方法区中,每个对象都可以访问。(方法区中存放的是类中的方法以及类中的共享数据) 

示例:

class Person{
private int age;
private String name;
//国籍相同,定义成静态成员,优化内存
static String country = "CN";
Person(String name,int age){
this.name = name;
this.age = age;
}
}
public class PersonDemo{
public static void main(String[] args){
Person p = new Person("xiaoqi",23);
}
}


1)被static修饰后的成员具备的特点:

(a)随着类的加载而加载。

        当Person类被使用时,会被加载到内存中,而此时,静态成员country就已经存在了。Person类只要还在,静态成员就会一直存在于内存中,直至类在内存中消失。

(b)优先于对象存在。

        Person类加载进内存中时,静态成员counrty就存在与内存中,而非静态成员name不存在,因为name只有在对象建立时才会存在于堆内存中。当对象建立并使用完毕后,name变不存在于堆内存中,而country依然存在。

(c)被所有对象所共享。

(d)可以直接被类名调用。(调用格式:类名.静态成员)

2)实例变量(对象变量/非静态变量)与类变量(静态变量)的区别

(a)存放位置

类变量随着类的加载而存在于方法区中。

实例变量随着对象的建立而存在于堆内存中。

(b)生命周期

类变量的生命周期最长,随着类的消失而消失。

实例变量的随着对象的消失而消失。

3)静态使用时的注意事项

(a)静态方法只能访问静态成员。(非静态方法既可以访问非静态成员又可以访问静态成员)

(b)静态方法中不可以定义this,super关键字。(因为静态优先于对象存在,this,super代表的是对象)

(c)何时使用静态:

静态分静态方法和静态成员:

当对象中出现共享数据时,该数据被静态所修饰。但对象特有的数据要定义成非静态存放于堆内存中。

当功能内部没有访问到非静态数据(对象特有数据)时,可以将该功能定义成静态的。

4)静态的利弊

利:对对象的共享数据进行单独存储,节省内存;可以直接被类名调用。

弊:生命周期过长;出现访问局限性(静态虽好,却只能访问静态)。因此不建议定义过多的静态成员。

2、main函数

 主函数是静态的。

public static void main(String[] args)

含义:

public  代表该函数的访问权限是最大的。

static  代表该函数随着类的加载就已经存在了。

void  代表主函数没有具体的返回值。

main  不是关键字,但是是一个特殊的单词,能被JVM识别。

String[] args  主函数的参数,参数类型是一个字符串数组。

        主函数是固定格式的,能被JVM识别,唯一能改变的就是参数名args;JVM在调用主函数是,传入的是new String[0]。可以在启动JVM时给主函数传递一些参数,格式如下:

java MainDemo zhang lisi

java  启动虚拟机

MainDemo  需要执行的类

zhang lisi  给MainDemo的主函数传递的参数

3、生成帮助文档

        当类中的成员都是静态成员时,该类就是一个工具类。为方便其他程序员使用该类中的功能,需要生成帮助文档,Java中提供了这样的工具:javadoc。(DOS命令行)操作格式如下:

javadoc -d c:\myhelp -author -version ArrayTool

javadoc  启动文档生成工具

-d c:\myhelp  文档存放目录为c:\myhelp

-author -version  文档中包括做作者和版本

注意:

1、要用javadoc工具生成文档时,该类要加public的权限修饰符。

2、需要生成帮助文档的成员前要用/**  */在java源文件中注释好,而且其只提取被public修饰的成员前的注释文档。

3、文档注释中的特殊标识:

@author  作者

@version  版本

@param  参数

@return  返回值

示例:

/*
数组操作复习-----静态工具类

该工具类不需要建立对象 	---	构造函数私有化
方法都可以直接用类名调用(方法中均为访问到非静态数据)
*/

package day.day1;

/**
* 这是一个工具类,可用于对数据进行操作
* @author 陈滔
* @version v1.0
*/

public class ArrayTool {

private ArrayTool(){}

/**
* 在整数型有序数组中插入一个元素
* @param arr 接收一个int类型的数组
* @param key 需要插入的数
* @return 返回插入的位置
*
*/
public static int HalfSearch_1(int[] arr, int key) {
int max = arr.length-1;
int min = 0;
int mid;
while(min<=max){
mid = (max+min);
if(key>arr[mid])
min = mid+1;
else if(key<arr[mid])
max = mid-1;
else
return mid;
}

return min;//min = mid+1,若找的数不存在,其肯定在两个数之间 角标为mid+1
}

/**
* 查找整数型数组的指定元素
* @param arr 接收一个int类型的数组
* @param key 需要查找的数
* @return 返回该数的位置,不存在则返回 -1
*
*/
public static int HalfSearch(int[] arr, int key) {
int max = arr.length-1;
int min = 0;

int mid = (max+min)/2;
while(key!=arr[mid]){
if(key>arr[mid])
min = mid+1;
else if(key<arr[mid])
max = mid-1;

mid = (max+min)/2;

if(min>max)
return -1;
}
return mid;
}

/**
* 获取整数型数组中的最大值
* @param arr 接收一个int类型的数组
* @return 返回最大值
*
*/
public static int getArrayMax(int[] arr) {

int max = arr[0];
for(int x=0;x<arr.length;x++){
if(max<arr[x])
max = arr[x];
}
return max;
}

/**
* 对整型数组进行冒泡排序
* @param arr 接收一个int类型的数组
*
*/
public static void arrayBubbleSort(int[] arr) {

for(int x=0;x<arr.length-1;x++){

for(int y=0;y<arr.length-1-x;y++){
if(arr[y]>arr[y+1])
swap(arr,y,y+1);
}
}
}

/**
* 对整型数组进行选择排序
* @param arr 接收一个int类型的数组
*
*/
public static void arraySelectSort(int[] arr) {
// TODO Auto-generated method stub
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);
}
}
}

private static void swap(int[] arr, int x, int y) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}

/**
* 对整型数组进行打印
* @param arr 接收一个int类型的数组
*
*/
public static void printArr(int[] arr) {
System.out.print("[");
for(int x=0;x<arr.length;x++){
if(x==arr.length-1)
System.out.println(arr[x]+" ]");
else
System.out.print(arr[x]+", ");

}
}

}




 

4、静态代码块

1)格式:

static

{

执行语句;

}

2)静态代码块的特点

(a)随着类的加载而执行,只执行一次,并优先于构造函数执行。

(b)用于给类进行初始化。

一个类中可以有多个静态代码块和构造代码块。

5、对象的初始化过程

Person p = new Person("zhangsan",20);

这句话在内存中完成了以下8个动作:

1)因为new用到了Person.class,所以会先将Person.class加载到内存中。

2)执行类中的静态代码块,给Person.class进行初始化。(如果静态代码块存在)

3)在堆内存中开辟空间,分配内存地址值。

4)在堆内存中建立对象的特有数据,并进行默认初始化。(null,0)

5)对属性进行显示初始化。

6)对对象进行构造代码块初始化。

7)对对象进行与之对应的构造函数初始化。

8)将内存地址值赋给栈内存中的p变量。

6、对象调用成员过程。

Person类中所有方法都存在与方法区。

图例:





七、单例设计模式

设计模式:早期在建筑领域出现的概念,也就是总结得一些经验与方法,模式的出现让问题变得更简单。

Java中一共有23种设计模式,下面介绍单例设计模式。

 

单例设计模式:解决一个类在内存中只存在一个对象的方案。

步骤:

1)将构造函数私有化,禁止外部其他类建立对象。

2)在本类中创建一个本类对象。

3)提供一个公共方法以获取到本类对象。

单例设计模式有有两种实现方式:

1、饿汉式

对象一加载,方法区就存在就有s,堆内存中就有对象。

public class Single{
//将构造函数私有化
private Single(){}
//在本类中创建本类对象
private static Single s = new Single();
//给外界提供一个公共的访问方式
public static Single getInstance(){
return s;
}
}


2、懒汉式

对象的延时加载。(类一加载s为空,且堆内存中还没有对象,只有调用方法的时候才建立对象。

public class Single {
private static Single s = null;
private Single(){}

public static Single getInstance(){

if(s==null){
synchronized(Single.class){
/*
* 多条语句操作共享数据,给这多条语句加上同一个锁,但是加上锁后效率变低
* 故再在外面加一层判断语句,以后就不会在判断锁了,提高了懒汉式的效率
*/
if(s==null)
s = new Single();//对象的演示加载
}
}
return s;
}
}


懒汉式的特点及存在的问题:

1)对象的延时加载。

2)延时加载在多线程访问时存在安全问题。

3)为解决多线程访问的安全的问题,加入同步synchronized,且其锁是该类对应的字节码文件对象。

4)加入同步之后会存在效率低的问题,因此加入双重判断语句。

饿汉式图解:



第二部分:面向对象的特点及其他

一、继承

1、继承的概述

        继承是面向对象的第二个特征。当多个类中出现共性功能时,将功能向上进行抽取,定义在父类中,让子类继承父类。父类中的功能子类可以直接继承、使用。

注意:千万不要不要为了获取其他类的功能而继承,必须是类与类之间有关系。(用关键字extends申明两个类之间的关系)

package demo;
//定义Person类
class Person
{
public void work()
{
System.out.println("work");
}
}
//定义Student继承Person
class Student extends Person
{

}
public class ExtendsDemo
{
public static void main(String[] args)
{
//父类Person中的方法,之类Student可以直接使用
new Student().work();
}
}


2、继承的特点

1)提高了代码的复用性。

2)继承的出现让类与类之间产生了关系,有了这个关系才有了多态的特性。

        Java语言中:只支持单继承,不支持多继承,因为多继承容易带来完全隐患。(即一个类中只要继承了一个类就不可以继承其他类)。但是Java保留了这种机制,并用另一种体现形式来表示,即多实现。同时,Java也支持多层继承。

如何使用一个继承体系中的功能呢?

        想要使用体系,先查阅父类中的功能,因为父类中定义的是该体系中的共性内容,通过了解共性内容就可以知道该体系的基本功能。查阅到父类功能后,建立子类对象去使用这些功能。因为有可能父类不能创建对象(抽象类等),创建子类对象可以使用子类中的特有功能。(查阅父类功能,创建子类对象使用功能

3、子父类中成员变量的特点。

(a)super关键字:代表父类对象的引用。super的使用与this的使用基本一致。(this关键字代表本类对象的引用)

(b)如果子父类中出现了非私有的同名变量时,子类要访问本类中的变量用this,子类要访问父类中的变量,用super。

4、函数覆盖(子父类中函数的特点)

        当子类出现和父类一模一样的函数时,若子类要调用该函数,会运行子类对象的内容,如同父类的函数被覆盖一下。这就是函数的另一个特性,覆盖。(父类依然存在,只是没有运行)

        当子类继承了父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时没有必要定义新功能而是使用覆盖特性,保留覆盖的功能,并重写功能的内容。

示例代码:

/*
* 子父类中构造函数的特点
*/
class Phone{
public void show(){
System.out.println("show-number");
}
}
class NewPhone extends Phone{
//覆盖父类中的show方法
public void show(){
//在原来的功能上增加新的功能
super.show();
System.out.println("show-video");
}
}


函数覆盖时注意事项:

1)子类覆盖父类,必须保证子类权限大于或等于父类权限,才可以覆盖,否则编译失败。

2)静态只能覆盖静态。

函数重载与覆盖的区别:

1)重载:只看函数的参数列表。

2)重写:子父类方法要一模一样(包括参数列表,返回值类型与函数名)。

以下两种情况不是覆盖:

父类:private void show()

子类:public void show() 子类不知道父类有该功能

 

父类:int show()

子类:void show() 编译错误,因返回值类型不同

5、子类的实例化过程(子父类构造函数的特点)

        在子类对象进行初始化时,父类的构造函数也会运行,那是因为子类构造函数第一行有一条隐式语句super()会访问父类中空参数的构造函数,而且子类所有的构造函数默认第一行都是super()。

为何子类一定要访问父类中的构造函数?

       因为父类中的数据子类可以直接获取,子类对象在建立时需要先查看父类是如何对这些数据进行初始化的,所以子类在进行初始化时会访问父类中构造函数,具体访问哪一个可以用super语句去指定。子类构造函数的第一行也可以手动指定this语句去访问本类中的构造函数,但必须要有一个构造函数访问父类中的构造函数。

示例代码:

class Animal
{
Animal(int num)
{
System.out.println("eat");
}
}
class Cat extends Animal
{
Cat(int num)
{
super(num);//访问父类中指定参数的构造函数
}
}


6、final关键字

1)final可以修饰类、函数及变量。

2)被final修饰的类不可以被继承,以避免该类被继承,功能被复写后产生问题。(如,直接操作虚拟机的类)

3)被final修饰的方法不可以被复写。

4)被final修饰的变量是一个常量,只能赋值一次。

作为常量,书写时所有字母都大写,多个单词组成时中间加”_“连接。

全局常量:如 public static final double PI = 3.14;

5)内部类定义在局部位置上时,只能访问该局部被final修饰的成员。

7、抽象类

        当多个类中出现相同功能,当功能主体不同,这时可以进行向上进行抽取,只抽取功能主体,不抽取功能定义。

抽象类的特点:

1)抽象方法一定定义在抽象类中。

2)抽象方法和抽象类必须被abstract修饰。

3)抽象类不可以new对象,因为抽象方法调用无意义。

4)抽象类中的方法要被调用,必须由子类复写所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。但抽象类中可以有非抽象方法。

 

抽象类与一般类的区别:

        抽象类与一般类并无太大区别,该如何描述事物就如何描述事物,只不过该事物出现了一些看不懂的东西,这些不确定的部分,也是该事物的功能,需要明确出来,但是无法定义主体,故将其通过抽象方法表示。

        抽象类比一般类多个个抽象方法,故抽象类不可以实例化。

 

8、Objdect类

        Object是Java中所有类的父类(或超类),所有对象(包括数组)都继承这个类的方法。

常用方法:

boolean equals(Object obj)        判断两个对象是否相等,子类不复写则判断的是哈希值是否相等)

int hashCode()        返回对象的内存地址值,哈希值

String toString()        返回对象的字符串表示形式,getClass().getName()+"@"+Integer.toHexString(hashCode())

Class<?> getClass()       返回该类所对应的字节码文件对象

多线程中的常用方法(Object类中):

void wait()        当前线程等待

void notify()        唤醒线程池中的第一个线程

void notifyAll()       唤醒线程池中的所有线程

二、模板方法模式

        模板方法模式就是在定义功能时,功能的一部分是确定的,另一部分是不确定的,但是确定的部分在使用不确定的部分,那么就将不确定的部分暴露出去,由子类复写来完成。(不确定的部分可以定义成抽象方法,确定的部分可以加final修饰)

示例代码:

package day.day1;
/*
需求:定义一个类来计算程序运行的时间
思路:
1、通过System类来获取当前时间
2、将要运行的代码封装成一个函数,由于需要运行的代码不确定,故定义成抽象的
3、由于该类中定义了抽象方法,因此定义一个子类继承该类,并复写掉抽象方法
4、建立子类对象去实现该功能。
*/
abstract class GetTime{
void runTime(){
//获取开始运行的时间
long start = System.currentTimeMillis();
runCode();//运行代码
//获取接收运行的时间
long end = System.currentTimeMillis();
System.out.println("程序运行"+(end-start)+"毫秒");
}
//运行代码确定,定义成抽象的
abstract void runCode();
}

class SubTime extends GetTime{
//子类复写掉父类中的抽象方法runCode(),将子类需要运行的代码定义在该函数中
void runCode(){
for(int x=0;x<1000;x++){
System.out.println(x);
}
}
}
public class AppTime {
public static void main(String[] args){
//创建子类对象去完成计算程序运行时间的功能
new SubTime().runTime();
}
}


结果为:



三、接口(interface)

1、定义

        接口是一个特殊的抽象类,类中的方法都是抽象的,变量都是常量。Java中用关键字class来定义类,用interface来定义接口。

2、接口的格式

1)接口中常见定义:常量,抽象方法。

2)接口中成员修饰符都是固定的。(若忘记写了,系统会自动补齐)

成员常量:public static final

成员方法:public abstract

3、继承与实现的区别

类与类之间的关系:extends(父类有非抽象方法可以给子类使用)

类与接口之间的关系:implements(接口中的方法都是抽象方法,子类若想实现则需全部复写,接口也不可以直接创建对象。

接口与接口之间的关系:extends

 

4、多实现

1)接口可以被多实现,这也是java对多继承不支持的转换形式。

2)在Java中,一个类可以在继承一个类的同时实现多个接口。

3)接口与接口之间可以多继承。(因为都是抽象方法,没有方法体)

5、接口的特点:

1)接口是对外暴露的规则。

2)接口时程序功能的扩展。

3)接口降低了程序之间的耦合性。

四、多态

1、多态的基本体现

父类引用指向子类对象。(父类引用接收子类对象)

示例代码:

abstract class Animal
{
public abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("猫吃鱼");
}
public void catchMouse()
{
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("狗吃骨头");
}
public void kanJia()
{
System.out.println("狗看家");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
//父类引用指向子类对象--类型提升
Animal a1 = new Cat();
Animal a2 = new Dog();
}
}


2、多态的好处

提高了程序的扩展性。

3、多态的前提

必须是类与类之间有关系,继承或实现。(通常还有一个前提,就是存在覆盖)

4、多态的弊端

只能使用父类引用访问父类成员。

如想访问子类中特有的方法,则应该进行类型转换,如:

Cat c = (Cat)a1;

c.catchMouse();

千万不要出现将父类对象转换成子类类型。如:

Animal a = new Animal();

Cat cat = (Cat)a;//报错,类型转换异常

新关键字:instanceof 判断某一类型的指向是否符合指定类型。

如:boolean flag = a instanceof Cat,判断a的引用是否指向Cat类型。

5、多态使用中的注意事项

1)在多态中,非静态成员函数的特点:编译看左边,运行看右边。

2)在多态中,成员变量的特点:无论编译和运行,都看左边。(针对重名的请况)

3)在多态中,静态成员函数的特点:无论编译运行,都看左边。

6、多态的应用

示例代码:

package day.day1;
/*
* 接口与多态的应用
* 接口降低了程序间的耦合性
*/
interface PCI {//定义接口,对外暴露规则
public abstract void open();
public abstract void close();
}
//定义主板
class MainBoard{
public void run(){
System.out.println("主板\trun");
}

//使用多态的特性,完成程序功能的扩展,如PCI p = new NetCard();
public void usePCI(PCI p){
if(p!=null){
p.open();
p.close();
}
}
}
//新进来的硬件只要实现接口,就能被主板所使用
class NetCard implements PCI{
public void open(){
System.out.println("网卡\topen");
}
public void close(){
System.out.println("网卡\tclose");
}
}

class VoiceCard implements PCI{

public void open(){
System.out.println("声卡\topen");
}
public void close(){
System.out.println("声卡\tclose");
}
}

public class MainBoardDemo{
public static void main(String[] args){
//新建一个主板对象
MainBoard mb = new MainBoard();
mb.run();
mb.usePCI(new NetCard());//使用网卡
mb.usePCI(new VoiceCard());//使用声卡
}
}


运行结果:



图例:



五、内部类

将一个类定义在另一个类的里面,里面的那个类就称为内部类(内置类或嵌套类)。

示例代码:

class Outer
{
private  static int x = 4;
//内部类
class Inner
{
void show()
{
System.out.println("Inner.show");
}
}
//静态 内部类
static class StaticInner
{
static void function()
{
System.out.println("StaticInner:funchtion");
}
public static void method()
{
System.out.println("StaticInner:method");
}
}
}


1、访问特点

1)内部类可以直接访问外部类中的成员,包括私有成员。(持有一个引用:外部内名.this)

2)外部类要访问内部类中的成员则必须建立内部类对象。

2、访问格式

1)当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。格式为:

外部类名.内部类名 变量名 = 外部类对象。内部类对象

Outer.Inner inner = new Outer().new Inner();

2)当内部类定义在外部类成员位置上时,可以被成员修饰符修饰,如:

private:将内部类在外部类中进行封装

static:内部类具备了静态的特性

3、内部类使用的注意事项

1)当内部类被static修饰时,只能访问外部类中被static修饰的成员

在外部其他类中,访问静态内部类中的非静态成员:new Outer.StaticInner().function();(通过内部类对象进行访问)

在外部其他类中,访问静态内部类中的静态成员:Outer.StaticInner.method();(都是静态的, 可以直接被类名调用)

2)当内部类中定义了静态成员,该内部类必须是静态的。

3)当外部类中的静态成员访问到内部类时,该内部类也必须是静态的。

4)当内部类在局部位置时,使用时要注意以下两点:

(a)不能被成员修饰符private,static等修饰。

(b)可以直接访问外部类中的成员,因为还只有外部类的引用(外部类名.this),但是只能访问它所在局部中被final修饰的局部变量。

何时使用内部类?

当描述事物时,事物的内部还有事物,内部事物就用内部类来描述。

4、匿名内部类

1)匿名内部类其实就是内部类的简写格式。

2)定义匿名内部类的前提:内部类必须是继承一个类或实现接口。

3)匿名内部类的格式:

new 父类名或接口名(构造函数的参数)

{

定义子类内容;(一般是复写父类中的方法)

}

4)其实内部类就是一个匿名子类对象,只是将定义类和建立对象封装成了一个整体。

5)匿名内部中定义的方法最好不要超过3个。

示例代码:

/*
需求:在没有父类和接口的情况下,如何通过匿名内部类运行其中的show方法。
分析:所有类的父类都是Object,因此可以通过Object来做。
*/
class InnerDemo
{
public static void main(String[] args)
{
new Object()
{
public void show()
{
System.out.println("show--run");
}
}.show();
}
}


运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: