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

------<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{
	// 在本类中创建一个本类对象的引用指向null
	private static Single2 s = null;
	// 私有化构造函数,让外界无法通过new创建对象
	private Single2(){
		
	}
	// 当调用Single2方法时,如果引用为null,则创建对象,不为null,返回引用s
	public 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...5
		z.show2();	//5...5
		z.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 run
		z.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;
	// 方法都是抽象的,且权限是public
	public abstract void show();
	public abstract void run();
}
接口中的成员都是public,方法都是抽象的。

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

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

// 接口
interface Inter{
	// 接口中定义的都是全局常量
	public static final double MY_PI = 3.14;
	// 方法都是抽象的,且权限是public
	public 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和接口B
interface 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的特有方法hawl
		a.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方法,编译不会失败,因为其继承了父类的show
	void 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方法,编译不会失败,因为其继承了父类的show
	static 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。this
class 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值