黑马程序员----JAVA基础----面向对象基础篇

来源:互联网 发布:teamviewer mac 破解版 编辑:程序博客网 时间:2024/06/16 03:44

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、面向对象

1,面向对象是一种常见思想,符合人们的思考方式,将复杂的问题简单

主要关注事物的两个方面:属性和行为。

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

类:事物的描述。对象:该类事物的实例,在java中通过new来创建。一个简单示例如下:

public class ObjectDemo {public static void main(String[] args) {// 通过new关键字创建一个Car的实例Car car = new Car();// car是一个类类型的变量,指向该类对象// 使用对象中的内容可通过 对象.成员的形式来完成car.num = 4;car.color = "black";car.run();<span style="white-space:pre"></span>// 4...black}}// 类:用来描述事物,主要关注属性和行为两个方面。class Car{// 属性---成员变量int num;String color;// 行为---成员函数void run(){System.out.println(num+"..."+color);}}

2,成员变量与局部变量的区别

a,成员变量定义在类中,整个类中都可以访问;局部变量定义在函数、语句和局部代码块中,

只在所属区域有效。b,成员变量存储在堆内存对象中;局部变量存储在栈内存的方法 中。c,

成员变量随着对象创建而存在,随着对象的消失而消失。局部变量随着所属区域的执行而存在,

随着所属区域的结束而释放。d,成员变量有默认初始化值,局部变量没有默认初始化值。

3,类类型的变量一定会指向对象,否则就指向null。

4,匿名对象:没有名字的对象,其实是定义对象的简化格式。

a,当对象对方法仅进行一次调用的时候,就可以简化成匿名对象。

b,匿名对象可以作为实际参数进行传递。

5,面向对象三大特征:封装、继承和多态。

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

好处:提高安全性、便于使用,提高复用性。

封装的原则:不需要对外提供的内容都隐藏起来;把属性都隐藏,仅对外提供公共方法对其访问。

代码示例:

public class ObjectDemo2 {public static void main(String[] args){Person1 p = new Person1();p.setAge(1000);// 填写1000就会提示:年龄错误}}class Person1{// private:是一个权限修饰符,用于修饰成员,私有内容只在本类中有效// 私有是封装的一种体现private int age;// 函数是java中最小的封装体public void setAge(int age) {// 对外提供的访问方法可以对属性进行限制if(age<0||age>150){System.out.println("年龄错误");return;}this.age = age;}}
6,构造函数:

特点:函数名与类名相同;不用定义返回值类型;没有返回值。

作用:用于给对象进行初始化。创建对象都必须要通过构造函数进行初始化。

在一个类中如果没有定义构造函数,那么该类中会有一个默认的空参构造函数。

如果类中定义了指定的构造函数,那么类中就没有默认的空参构造函数。如果还需要

空参构造函数,那么就显式定义空参构造函数。代码示例:

class Person{// 定义成员变量,并私有化private int age;private String name;// 构造函数与类名相同,没有返回值类型// 构造函数是用于给对象进行初始化。Person(){// 定义构造函数,空参System.out.println("Hello World!");// 没有返回值}// 定义成员函数,对外提供访问方法public void speak(){System.out.println(name+"---"+age);}}
7,一般函数与构造函数的区别

a,构造函数:对象创建时,就会调用与之对应的构造函数,对对象进行初始化。

一般函数:对象创建后,需要函数功能时才调用。

b,构造函数:对象创建时,会调用且只调用一次。

一般函数:对象创建后,可按需求进行多次调用。

8,什么时候定义构造函数?

在描述事物时,该事物一存在就具备的一些内容,这些内容应该定义在构造函数中。

构造函数可以有多个,用于对该类对象进行针对性的初始化,多个构造函数在类中

以重载的形式体现。注意:构造函数带有return语句。演示代码:

class Person{// 定义成员变量,并私有化private int age;private String name;// 构造函数重载:参考函数名和参数列表(参数个数、参数类型、参数顺序)Person(){// 空参构造函数}Person(int age){// 带1个参数构造函数}Person(String name){// 带1个参数构造函数}Person(int age,String name){// 带2个参数构造函数}Person(String name,int age){// 带2个参数构造函数}}
9,this关键字

当成员变量和局部变量重名时,可以用this关键字来区分。

this就是所在函数所属对象的引用,代表的是当前对象。简单说,哪个对象调用了

this所在的函数,this就代表哪个对象。

class Person{// 定义成员变量,并私有化private int age;private String name;// 当成员变量和局部变量重名时,可以用this关键字来区分。Person(String name,int age){this.name = name;this.age = age;}}
this也可以用在构造函数中调用其他构造函数,但是只能定义在第一行。演示代码:

class Person{// 定义成员变量,并私有化private int age;private String name;// 一个参数的构造函数Person(String name){this.name = name;}// 两个参数的构造函数Person(String name,int age){this(name);//调用其他构造函数,定义在第一行this.age = age;}}
总结:构造函数可以调用一般函数;一般函数可以调用一般函数;

一般函数一般不调用构造函数;构造函数可以调用构造函数(使用this关键字)

10,static关键字

static特点:a,static是一个修饰符,用于修饰成员。b,static修饰的成员被所有该类对象共有

c,static优先于对象存在,因为static成员随着类的加载而加载。d,static修饰的成员可以被类名

所有调用,调用格式:类名.成员。e,静态修饰的数据时共享数据,对象中存储的是特有数据。

静态变量和成员变量的区别:a,生命周期不同,成员变量随着对象的创建/消失而创建/消失。

b,静态变量随着类的加载/消失而加载/消失。b,调用方式不同,成员变量只能被对象调用;静态

变量既可以被对象调用,又可以被类名调用,建议用类名调用。c,存储位置不同,成员变量存储

在堆内存的对象中,又称对象的特有数据;静态变量存储在方法区(的静态区),又称对象的共享

数据。d,别名不同:成员变量又称实例变量;静态变量又称类变量。

静态使用的注意事项:a,静态方法只能访问静态成员。b,非静态方法既可以访问静态,又可以访问

非静态。c,静态方法中不可以定义this、super关键字。d,主函数是静态的。

class Person{private int age;// 成员变量--实例变量private static String name;// 静态变量--类变量// 静态方法只能调用静态成员,静态方法中没有this关键字public static void show(){System.out.println(/*this.*/name); // this不可以用}// 一般方法既可以调用静态成员,又可以调用非静态成员public void method(){System.out.println(name+"..."+age);}}
静态什么时候使用?

静态成员:成员是所有对象共有的,这时可以定义为静态成员

静态方法:方法中只要不涉及到非静态变量,就可以定义为静态成员。

静态代码块:定义在类中,随着类的加载而加载,用于给类初始化,而且只执行一次。

class StaticDemo{// 静态代码块,随着类加载而加载,只执行一次static{System.out.println("hello world ");}}
11,构造代码块:定义在类中,随着对象加载而加载,用于给所有对象初始化,可执行多次。

构造代码块与构造函数都是给对象进行初始化,二者的区别是什么呢?构造代码块是用于给所有的

对象进行初始化,而构造函数是对该类指定对象进行特定初始化。演示代码:

class ConsDemo{String name;// 构造代码块:用于给该类所有对象进行初始化{System.out.println("haha");}// 给空参对象进行初始化ConsDemo(){System.out.println("heihei");}// 给带name的独享进行初始化ConsDemo(String name){this.name = name;}}
一个类中所有的方法都是静态的,那么就可以将该类的构造函数全部私有化。

12,单例设计模式:保证一个类的对象在内存中的唯一性。如何保证对象的唯一性呢?

思路:a,不允许使用new来创建该类对象。b,在该类中创建一个本类实例并私有化。

c,对外提供一个方法让其他程序可以获取该对象。

// 饿汉式<span style="white-space:pre"></span>开发时用class Single{// 在本类中创建一个本类对象并私有化private static Single s = new Single();// 私有化构造函数,让外界无法通过new创建对象private Single(){}// 对外提供访问本类对象的方法。public static Single getSingle(){return s;}}// 懒汉式,存在一定的安全隐患,多线程技术可解决此问题class Single2{// 在本类中创建一个本类对象的引用指向nullprivate static Single2 s = null;// 私有化构造函数,让外界无法通过new创建对象private Single2(){}// 当调用Single2方法时,如果引用为null,则创建对象,不为null,返回引用spublic static Single2 getSingle(){if(s==null)s = new Single2();return s;}}
二、面向对象特征之二:继承(extends)

1,继承的好处:提高了代码的复用性;让类与类之间产生了关系;为多态提供了前提。
在java中支持单继承,不支持多继承,对C++中的多继承机制进行了改良(多实现)。

单继承:一个子类只能有一个直接父类。多继承:一个子类可以有多个直接父类。

为什么不支持多继承?因为多个父类中可能有相同的成员,会产生调用的不确定性。

当使用一个继承体系时,查看顶层类,了解基本功能;创建最子类对象,完成功能调用。

什么时候使用继承?

当类与类之间存在所属关系时,可以定义继承。is a的关系

2,子父类中继承关系的体现三个方面:成员变量、成员函数、构造函数。

成员变量:

public class ObjectDemo2{public static void main(String[] args){Zi z = new Zi();z.show1();//4...5z.show2();//5...5z.show3();//3...5}}class Fu{int num = 3;int num1 = 4;}class Zi extends Fu{int num = 5;// 子父类中成员变量不同名时,可以正常访问父类中的成员变量void show1(){System.out.println(num1+"..."+num);}// 子父类中成员变量同名时,不可以直接访问父类中的成员变量void show2(){System.out.println(num+"..."+num);}// 子父类中成员变量同名时,访问父类成员变量需要加上super关键字void show3(){System.out.println(super.num+"..."+num);}}

super是代表父类空间,而不是父类对象,另外父类成员变量存储在子类对象中。

成员函数:

public class ObjectDemo2{public static void main(String[] args){Zi z = new Zi();// 子父类中函数不同名时,如果子类中没有,就去父类中找z.show1();// fu show runz.show2();// zi show run// 子父类中成员函数一模一样时,会运行子类函数。这种现象称为覆盖操作z.show();// zi show run ...}}class Fu{void show(){System.out.println("fu show run...");}void show1(){System.out.println("fu show run");}}class Zi extends Fu{void show(){System.out.println("zi show run...");}void show2(){System.out.println("zi show run");}}
对于子父类中的特性覆盖有以下注意事项:

a,子类方法覆盖父类方法时,子类权限必须大于父类权限。

如果父类的同名方法时private时,这时不称为覆盖

b,静态只能覆盖静态或被静态覆盖。
覆盖操作什么时候用?

当对一个类进行子类功能扩展时,子类需要保留父类的功能声明,但是要定义子类中该

功能特有内容时,就是用覆盖操作来完成。演示代码:

public class ObjectDemo2{public static void main(String[] args){new NewPhone().show();}}class Phone{void show(){System.out.println("call");}}// 子类对象对父类对象功能的扩展class NewPhone extends Phone{// 覆盖父类中的show方法void show(){System.out.println("image");System.out.println("color");super.show();}}
构造函数:

子类构造函数中第一行有一个默认的隐式语句super(),调用的是父类中的空参构造函数。

如果父类中没有空参构造函数,那么子类就需要显示指定父类某个构造函数super(para1,para2...)

public class ObjectDemo2{public static void main(String[] args){Zi z = new Zi();<span style="white-space:pre"></span>//  fu run   zi run}}class Fu{Fu(){System.out.println("fu run");}}class Zi extends Fu{Zi(){// super();<span style="white-space:pre"></span>//默认的隐式语句System.out.println("zi run");}}
当父类中没有空参构造函数时:

public class ObjectDemo2{public static void main(String[] args){Zi z = new Zi();<span style="white-space:pre"></span><span style="font-family: Arial;">//  fu run   zi run</span>}}class Fu{Fu(int x){System.out.println("fu run");}}class Zi extends Fu{Zi(){super(3);<span style="white-space:pre"></span>// 显示指定父类的构造函数System.out.println("zi run");}}
为什么子类对象实例化过程要访问父类的构造函数呢?

因为子类继承了父类,获取了父类中的内容,所以在使用父类内容之前,需要知道父类是如何对

自己进行初始化的,所以子类在创建对象时,必须访问父类中的构造函数,通过super语句实现。

super语句放在第一行的原因:父类必须先完成初始化。

当子类构造函数中有this语句时(必须放在第一行),那么这个构造函数中就没有super了,但可以

肯定的是,子类中某个构造函数中肯定含有super语句。

3,final关键字:可修饰类、方法、变量

final修饰的类不可被继承,修饰的方法不可被覆盖,修饰的变量为常量,只能赋值一次。

此外,内部类只能访问由final修饰的局部变量。

被final修饰的变量,一般也由static修饰,同时要显示初始化。而且一般起一个全为大写的名称。

如果由多个字母组成,中间由”-“连接。

4,抽象类:类由abstract修饰

// 抽象类abstract class Demo{// 抽象方法,没有方法体abstract void show();}
抽象类特点:

a,方法只有声明没有实现时,该方法就是抽象方法,需要被abstract修饰。抽象方法必须放

在抽象类中,该类也必须被abstract修饰。

b,抽象方法不可以被实例化,因为调用抽象方法没有意义。

c,抽象类必须由其子类覆盖了所有的抽象方法后,才可以对其子类进行实例化。如果没有覆盖

所有的抽象方法,那么该子类仍然是抽象方法。

抽象类的注意事项:

a,抽象类中有构造函数,用于给其子类进行初始化。

b,抽象方法可以不定义抽象方法,目的是不让该类创建对象,AWT适配器。

c,抽象关键字不可以与private,final、static(类名调用抽象方法没有意义)。

d,抽象类与一般类的异同:二者都是用来描述事物的,都在内部定义了成员。

不同的是:一般类有足够的信息描述事物,抽象类描述事物的信息可能不足;一般类中

不能定义抽象方法,只能定义非抽象方法,抽象类都可以定义;一般类可以被实例化,而

抽象类不可以被实例化。

e,抽象类一定是父类吗?是的,如果子类没有完全覆盖父类中的抽象方法,

那么子类仍然是抽象方法。

5,接口:由interfacce修饰。

当一个类中所有的方法都是抽象方法时,这时这个类可以由接口来表示。

// 接口interface Inter{// 接口中定义的都是全局常量public static final double MY_PI = 3.14;// 方法都是抽象的,且权限是publicpublic abstract void show();public abstract void run();}
接口中的成员都是public,方法都是抽象的。

类与接口之间的关系为实现关系,通过关键字implements,一个类实现一个接口,那么

必须覆盖该接口中所有的抽象方法。

// 接口interface Inter{// 接口中定义的都是全局常量public static final double MY_PI = 3.14;// 方法都是抽象的,且权限是publicpublic abstract void show();public abstract void run();}class Impl implements Inter{// 覆盖接口中所有的抽象方法public void show() {}public void run() {}}
一个类可以实现多个接口,弥补了java单继承的局限。

一个类可以继承另一个类的同时,还可以实现多个接口。

interface A{public abstract void show();}interface B{public abstract void run();}// 接口C同时继承了接口A和接口Binterface C extends A,B{public abstract void method();}
接口的特点:

接口对外暴露规则,降低了耦合性,实现了功能的扩展。

接口和抽象类的异同点:

相同点:都是不断向上抽取而的得到的。

不同点:

a,抽象类需要被继承,而且只能单继承;接口需要被实现,可以多实现。

b,抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。

接口中只能定义抽象方法,必须由子类去实现。

c,抽象类的继承是is a关系,在定义体系的基本共性内容。

接口实现是like a关系,在定义体系的额外功能。

接口的应用练习:

public class PC{public static void main(String[] args){useUSB(new Upan());// 功能得到了扩展useUSB(new Umouse());}public static void useUSB(USB usb){usb.open();usb.close();}}interface USB{// 对外暴露规则public abstract void open();public abstract void close();}class Upan implements USB{// 实现了规则public void open() {System.out.println("Upan open");}public void close() {System.out.println("Upan close");}}class Umouse implements USB{//实现了规则public void open() {System.out.println("Umouse open");}public void close() {System.out.println("Umouse close");}}
三、多态: 父类或者接口指向子类的对象。(简单说:一个对象,两个形态)

1,多态的好处:提高了代码的扩展性,前期定义的代码可以使用后期的内容。

多态的弊端:前期定义的代码不可以使用后期子类的特有内容。

多态的前提:必须有继承或实现关系;必须有覆盖操作。

2,多态的形态转换

父类或接口引用指向子类对象时,子类对象就得到了自动提升(向上转型),这时引用

只能使用子父类中的共性方法。如果想使用子类中的特有方法,则需要讲引用进行向下

转型。演示代码:

public class DuoTaiDemo{public static void main(String[] args){// Dog类型被自动提升为了动物类型,向上转型Animal a = new Dog();// a只能访问共性方法eat,而不能访问Dog的特有方法hawla.eat();// 如果要访问子类中的特有方法,需要向下转型// 向下转型的目的就是使用子类中的特有方法if(a instanceof Dog){// 一般在转型时,都会加入类型判断Dog d = (Dog)a;d.hawl();}}}abstract class Animal{public abstract void eat();}// 继承了Animal抽象类class Dog extends Animal{// 共性方法public void eat() {System.out.println("dog eat");}// 特有方法public void hawl(){System.out.println("dog hawl");}}
3,多态--成员特点:

成员变量:

编译和运行都看左边(父类或接口引用),即父类或接口中是否含有调用的成员变量。

public class DuoTaiDemo{public static void main(String[] args){// 如果父类中没有num则编译失败Fu f = new Zi();System.out.println(f.num);//3}}class Fu{int num = 3;}class Zi extends Fu{int num = 5;}
成员函数:编译看左边,运行看右边。

public class DuoTaiDemo{public static void main(String[] args){// 编译看左边,运行看右边Fu f = new Zi();f.show();}}class Fu{// 如果父类中没有show方法,则编译失败。void show(){System.out.println("fu show");}}class Zi extends Fu{// 子类没有自己的show方法,编译不会失败,因为其继承了父类的showvoid show(){System.out.println("zi show");}}
静态函数:和成员变量一样,编译和运行都看左边。

public class DuoTaiDemo{public static void main(String[] args){// 编译看左边,运行看左边Fu f = new Zi();f.show();}}class Fu{// 如果父类中没有show方法,则编译失败。static void show(){System.out.println("fu show");}}class Zi extends Fu{// 子类没有自己的show方法,编译不会失败,因为其继承了父类的showstatic void show(){System.out.println("zi show");}}
四、内部类

1,将一个类定义在另一类的内部,这个定义在内部的类称为内部类。

内部类可以直接访问内部类成员包括私有成员。外部类要访问内部类需要创建内部类对象。

public class ClassDemo {public static void main(String[] args) {new Outer().show();// 如果内部类不是私有的,可以直接访问内部类new Outer().new Inner().show();}}class Outer{private int num = 3;class Inner{void show(){// 内部类可以访问外部类的成员包括私有的成员System.out.println("Outer's private num is "+num);}}// 外部类访问内部类,需要先创建内部类对象void show(){new Inner().show();}}

2,内部类定义在外部类成员位置上,可以由权限修饰符进行修饰。
如果内部类是private,那么再其他类中无法直接访问内部类。

如果内部类是static,相当于一个外部类

如果内部类是static,且定义有static方法,可有外部类名.内部类名.静态方法调用。

import Object.Outer.Inner;public class ClassDemo {public static void main(String[] args) {new Outer().show();// 如果内部类不是私有非静态的,可以直接访问内部类//new Outer().new Inner().show();// 如果内部类是静态的//Inner in = new Outer.Inner();// 如果内部类是静态的,且有静态成员Outer.Inner.function();}}class Outer{private static int  num = 3;static class Inner{void show(){// 内部类可以访问外部类的成员包括私有的成员System.out.println("Outer's private num is "+num);}// 内部类定义了静态成员,那么内部类也应该是静态的static void function(){System.out.println("function run");}}// 外部类访问内部类,需要先创建内部类对象void show(){new Inner().show();}}
3,内部类为什么能访问外部类?因为内部类持有外部类的引用 Outer.this

public class ClassDemo {public static void main(String[] args) {new Outer().new Inner().show();//5...4...3}}// 内部类为什么能访问外部类?因为内部类持有外部类的引用 Outer。thisclass Outer{int num = 3;class Inner{int num = 4;void show(){int num = 5;System.out.println(num+"..."+this.num+"..."+Outer.this.num);}}}
4,内部类可以放到局部位置上。只能访问局部中被final修饰的局部变量。
public class ClassDemo {public static void main(String[] args) {new Outer().method();}}// 内部类可以放到局部位置上。只能访问局部中被final修饰的局部变量。class Outer{int num = 3;void method(){final int num = 6;class Inner{void show(){System.out.println(num);}}Inner in = new Inner();in.show();}}
5,匿名内部类:内部类的简写格式,其实是一个匿名子类对象。

匿名内部类的前提:内部类必须继承一个外部类或者实现一个接口。

通常使用的场景之一:当函数参数是接口类型时,而且接口中的方法不超过三个,可以

用匿名内部类作为实际参数进行传递。

public class ClassDemo {public static void main(String[] args) {// 接口的子类对象作为函数的参数进行传递new ClassDemo().run(new Inter(){public void show(){System.out.println("haha");}});}public void run(Inter in){in.show();}}// 接口interface Inter{public abstract void show();}
关于面向对象的总结:在定义类时,不需要对外暴露的全部封装,仅对外提供访问方法。

继承和接口是多态的前提。多态应用时注意:父类引用可以访问共性内容,不能访问子类

的特有内容。接口的出现降低了耦合性并使类扩展功能更加方便。

The End



0 0
原创粉丝点击