Java学习笔记(03面向对象)

知识点总结于毕向东Java基础视频教程25天版本,侵权请联系删除。

第三章:面向对象

  • 面向对象概念
    • 理解面向对象
    • 面向对象特点
    • 面向对象开发,设计,特征
  • 类与对象
    • 类与对象的关系 (图例)
    • 类的定义
    • 成员变量和局部变量的区别
    • 创建和使用对象
    • 对象内存结构
    • 匿名对象
  • 封装(Encapsulation)
    • 封装private
  • 构造函数
    • 构造代码块
  • this关键字
  • static关键字
    • static
    • main函数
    • 静态的应用
    • 静态代码块
  • 对象的初始化过程和对象调用成员过程
    • 对象的初始化过程
    • 对象调用成员过程
  • 单例设计模式
    • 饿汉式
    • 懒汉式

面向对象概念

理解面向对象

  • 面向对象是相对面向过程而言
  • 面向对象和面向过程都是一种思想
  • 面向过程强调的是功能行为
  • 面向对象将功能封装进对象,强调具备了功能的对象。
  • 面向对象是基于面向过程的。

面向对象特点

  • 是一种符合人们思考习惯的思想
  • 可以将复杂的事情简单化
  • 将程序员从执行者转换成了指挥者
  • 完成需求时:
              -先要去找具有所需的功能的对象来用。
              -如果该对象不存在,那么创建一个具有所需功能的对象。
              -这样简化开发并提高复用。

面向对象开发,设计,特征

  • 开发的过程:其实就是不断的创建对象,使用对象, 指挥对象做事情。
  • 设计的过程:其实就是在管理和维护对象之间的关系。
  • 面向对象的特征: 封装(encapsulation) 、继承(inheritance)、多态(polymorphism)

类与对象

类与对象的关系 (图例)

  • 使用计算机语言就是不断的在描述现实生活中 的事物。
  • java中描述事物通过类的形式体现,类是具体事物的抽象,概念上的定义。
  • 对象即是该类事物实实在在存在的个体。

类与对象的关系如图:
Java学习笔记(03面向对象)_第1张图片
可以理解为:

  • 类就是图纸
  • 汽车就是堆内存中的对象

类的定义

  • 生活中描述事物无非就是描述事物的属性行为
    如:人有身高,体重等属性,有说话,打球等行为。
  • Java中用类class来描述事物也是如此。
    属性:对应类中的成员变量。
    行为:对应类中的成员函数。
  • 定义类其实在定义类中的成员(成员变量和成员函数 )。

成员变量和局部变量的区别

  • 成员变量:
    –成员变量定义在类中,在整个类中都可以被访问。
    –成员变量随着对象的建立而建立,存在于对象所在的内存中。
    –成员变量有默认初始化值。
  • 局部变量:
    –局部变量只定义在局部范围内,如:函数内,语句内等。
    –局部变量存在于内存中。
    –作用的范围结束,变量空间会自动释放。
    –局部变量没有默认初始化值。

创建和使用对象

class Car//对Car这类事物进行描述 
{ 
	String color = "red"; int num = 4; 
	void show() 
	{ 
		System.out.println("color="+color+"..num="+num);
	} 
} 
class CarDemo 
{
 public static void main(String[] args) 
 { 
 	Car c = new Car();//建立对象 c.color = "black";//对对象的属性进行修改 c.show();//使用对象的功能。      
 }
}

对象内存结构

Java学习笔记(03面向对象)_第2张图片

匿名对象

  • 匿名对象是对象的简化形式
  • 匿名对象两种使用情况
    –当对对象方法仅进行一次调用的时
    –匿名对象可以作为实际参数进行传递

class Demo
{
	public static void main(String[] args){
		car c=new car();//在栈中加载主函数,主函数中有c并存储在堆中创建好的car对象的地址值,则c指向了对象
		showCar(c);//在栈中开辟showCar方法空间,包含方法中的变量。并接收c中对象的地址值,从而也指向同一个在堆中的对象,并修改了对象
		showCar(new car());//匿名对象可以作为实际参数进行传递。在堆内存中产生了一个对象并传递给了栈内存中函数showCar()空间中的变量c。即c指向了该对象,所以该对象不是垃圾。等到方法结束,内存释放。
		
		new car().run();//匿名对象,只在堆内存中开辟空间,没有在栈内存中的引用。调用对象中的方法。
	}

	public static void showCar(car c){
		c.wheel=3;
		c.color="blue";
		c.run();
	} 
}


class car
{
	int wheel=4;
	String color="red";
	
	void run(){
		System.out.println(wheel+" "+color);
	}
}

运行结果:

3 blue
3 blue
4 red

封装(Encapsulation)

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

  • 好处:
    1.将变化隔离。
    2.便于使用。
    3.提高重用性。
    4.提高安全性。

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

封装private

  • private关键字
    –是一个权限修饰符。
    –用于修饰成员(成员变量和成员函数)
    –被私有化的成员只在本类中有效。

  • 常用之一:
    –将成员变量私有化,对外提供对应的set ,get方法对其进行访问。提高对数据访问的安全性。

构造函数

特点:

  1. 函数名与类名相同。
  2. 不用定义返回值类型。
  3. 不可以写return语句。
  4. 不能被调用,只有在创建对象时调用。

作用:给对象进行初始化。
注意:

  • 默认构造函数的特点:不带参数。
  • 多个构造函数是以重载的形式存在的。

构造代码块

  • 对象一建立就运行,而且优先于构造函数执行

作用:给对象进行初始化。
和构造函数的区别:构造代码块是给所有对象进行统一初始化;而构造函数是给对应的对象初始化。

class Demo
{
	public static void main(String[] args){
			baby babe =new baby();
			baby babe1=new baby("张三",1);
	}

}

class baby
{
	private String name;
	private int age;

	{//构造代码块,所有对象初始化都会运行
		System.out.println("i'm crying...");
	}
	
	baby()
	{
		System.out.println(name+" "+age);
	}
	baby(String na,int nu)//构造函数重载
	{
		name=na;
		age=nu;
		System.out.println(name+" "+age);
	}

}

运行结果:

i'm crying...
null 0
i'm crying...
张三 1

this关键字

  • 特点:this代表其所在函数所属对象的引用。 换言之:this代本类对象的引用。

什么时候使用this关键字呢?

当在函数内需要调用该函数的对象时,就用this。

class Demo
{
	public static void main(String[] args){
			baby babe =new baby();
			baby babe1=new baby("张三",1);
	}

}

class baby
{
	private String name;
	private int age;

	{//构造代码块,所有对象初始化都会运行
		System.out.println("i'm crying...");
	}
	
	baby()
	{
		System.out.println(name+" "+age);
	}
	baby(String name,int age)//构造函数重载
	{
 		/*使用this关键字代表当前对象的成员变量name=局部变量name,
 		在上面的主函数中创建的两个对象中相当于babe.name=name/babe1.name=name;*/
		this.name=name;
		this.age=age;
		System.out.println(name+" "+age);
	}

}

运行结果:

i'm crying...
null 0
i'm crying...
张三 1

this语句:

  • 用于构造函数之间的互相调用。
  • 因为一般函数不能直接调用构造函数,所以this语句不能用在一般函数中。
  • 只能定义在构造函数的第一行,因为初始化要先执行。因此每个构造方法只能使用一次。

在带参数的构造函数baby中添加this语句:

	baby(String name,int age)//构造函数重载
	{
		/*
		this语句,指代执行当前对象的无参构造函数。
		this()相当于baby()
		*/
 		this(); 
 		/*使用this关键字代表当前对象的成员变量name=局部变量name,
 		在上面的主函数中创建的两个对象中相当于babe.name=name/babe1.name=name;*/
		this.name=name;
		this.age=age;
		System.out.println(name+" "+age);
	}

运行结果:

i'm crying...
null 0
i'm crying...
null 0
张三 1

注意:要避免两个构造函数之间使用this互相调用对方,这样会导致死循环。

static关键字

static

  1. 用于修饰成员(成员变量和成员函数)
  2. 被修饰后的成员具备以下特点:
    1.随着类的加载而加载,加载于方法区当中
    2.优先于对象存在
    3.被所有对象所共享
    4.可以直接被类名调用
  3. 静态的使用
    –什么时候定义静态变量?
      当对象中出现共享数据时,该数据被静态所修饰。(对象的特有数据是定义成非静态且存在堆中)
    –什么时候定义静态函数?
    当功能内部没有访问到非静态数据(对象的特有数据)。
  4. 使用注意
    1.静态方法只能访问静态成员(非静态方法既可以访问静态变量也可以访问非静态变量)。
    2.静态方法不可以写this,super关键字。
       因为静态对象优先于对象存在,由于this代表了当前对象,所以静态方法中不可以出现this。
    3.主函数是静态的。
    4.静态函数间的调用省略了"类名."。(非静态之间省略了"this.")

在baby类中添加static修饰的成员变量和成员函数

class Demo
{
	public static void main(String[] args){
			baby.show();//直接通过类名调用静态方法
	}
}

class baby
{
	private String name;
	private int age;
	static String country="china";
	{
		System.out.println("i'm crying...");
	}
	
	baby()
	{
		System.out.println(name+" "+age);
	}
	baby(String name,int age)
	{
		this();
		this.name=name;
		this.age=age;
		System.out.println(name+" "+age);
	}
	static void show()
	{
		System.out.println(country); //访问了静态变量
	}

}

静态的优点:

  1. 对对象的共享数据进行单独空间的存储,节省了空间。
  2. 可以直接被类名调用。

静态的缺点:

  1. 生命周期过长,静态过多耗费内存。
  2. 访问出现局限性。(静态只能访问静态)

main函数

主函数是一种特殊的函数。作为程序的入口,可以被jvm调用。

  • 主函数的定义
    public:代表着该函数访问权限是最大的。
    static:代表主函数随着类的加载而已经存在。
    void:主函数没有具体的返回值。
    main:不是关键字,属于被jvm识别的特殊单词。
    (String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。args为变量名,可更改。

主函数的格式是固定的,由jvm识别。

class Demo
{
	public static void main(String[] args){
			String[] arr={"1","2","3"};
			MainTest.main(arr);//调用MainTest类中的主函数

			/*在cmd编译好java文件后,在控制台输入java Demo haha并回车,会输出haha。
			这是因为虚拟机把类后的数据haha作为做函数的参数赋给了数组args[0]*/
			System.out.println(args[0]);
	}

}

class MainTest
{
	public static void main(String[] args){
		for(int i=0;i<args.length;i++)
			System.out.println(args[i]);
	}
}

执行语句:
Java学习笔记(03面向对象)_第3张图片
输出结果:
Java学习笔记(03面向对象)_第4张图片

静态的应用

每一个程序中都有共性的功能,我们可以将这些功能进行抽取,独立封装以便复用。所以可以将一些方法封装在一个类中。

class Tool
{
	Tool(){//构造函数,用于初始化对象
	}
	
	public int getMax(int[] arr){
		...
	}
	public int getMin(int[] arr){
		...
	}
	public int sort(int[] arr){
		...
	}
}

class Demo
{
	public static void main(String[] args){
		String[] arr={1,45,63,234,33};
		Tool tool=new Tool();
		tool.getMax(arr);
	}
}

但这样就有些问题:

  1. 对象是用于封装数据的,可是Tool对象并未封装特有数据。
  2. 操作数组的每一个方法都没有用到Tool对象的特有数据。

这时就可以考虑让程序更加严谨,该类是不需要对象的。

  • 可以将Tool中的方法都定义成static的。直接通过类名调用即可。

将方法都静态后,可以方便使用,但是该类还是可以被其它程序建立对象的。为了更加严谨,强制让该类不能建立对象。

  • 可以通过构造函数私有化完成。即private。
class Tool
{
	private Tool(){//构造函数私有化
	}
	
	public static int getMax(int[] arr){
		...
	}
	public static int getMin(int[] arr){
		...
	}
	public static int sort(int[] arr){
		...
	}
}

class Demo
{
	public static void main(String[] args){
		String[] arr={1,45,63,234,33};
		Tool.getMax(arr);
	}
}

静态代码块

随着类的加载就会执行且执行一次,执行优先级高于构造代码块和类中主函数。
格式:static{}

class Demo
{
	public static void main(String[] args){
		new StaticCode(4);//创建匿名对象并调用带参构造函数
	}

}

class StaticCode
{
	int num=9;
	StaticCode(){
		System.out.println("b");
	}

	static{ //静态代码块,当类加载时执行,所以优先执行
		System.out.println("a");
	}

	{//构造代码块,当对象被创建时执行,第二个执行
		System.out.println("c"+this.num);
	}

	StaticCode(int x){//构造函数,当对应的对象创建后后执行,最后执行
		System.out.println("d");
	}
}

执行顺序:

静态代码块/静态变量>构造代码块/成员变量>构造函数
(同级按照代码书写顺序执行)

对象的初始化过程和对象调用成员过程

对象的初始化过程

StaticCode s =new StaticCode(); 

该句话都做了什么事情?

  1. 因为new到了StaticCode.class,所以会先找到StaticCode.class文件并加载到内存中。
  2. 执行该类中的static代码块,如果有的话,就给StaticCode.class类进行初始化。
  3. 在堆内存中开辟空间,分配内存地址。
  4. 在堆内存中建立对象的特有属性,并进行默认初始化。
  5. 对属性进行初始化。
  6. 对对象进行构造代码块初始化。
  7. 对对象进行对应的构造函数初始化。
  8. 将内存地址赋给栈内存中的s变量。

对象调用成员过程

class Demo
{
	public static void main(String[] args){
		Person p=new Person("zhangsan",20);
		p.setname("lisi");
	}

}

class Person
{
	private String name;
	private int age;
	private static String country="china";

	Person(String name,int age){
		this.name=name;
		this.age=age;
	}

	public void setName(String name){
		this.name=name;
	}

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

	public static coid showCountry(){
		System.out.println("country="+country);
	}
}
  1. 现在栈中开辟主函数main的空间,里面包含p变量。
  2. 在方法区中开辟Person类空间,空间中加载静态成员和方法体。
  3. 在堆中创建对象,里面包含成员变量name,age并初始化,将对象地址值赋给p。
  4. 在栈内存中开辟方法setName空间,空间中有局部变量name和this(代表当前对象)。
  5. 将当前对象地址值赋给this,即现在this代表p所指向在堆内存中的对象。
  6. 把局部变量name的值赋给对象中成员变量name,所以zhangsan改为lisi。

单例设计模式

设计模式是解决每一类问题最行之有效的方法。而单例设计模式是解决一个类在内存中只存在一个对象。保证对象唯一性。

  • 为了避免其它程序过多建立该类对象,先禁止其他程序建立该类对象。
  • 为了让其他程序可以访问到该类对象,需要在本来中先自定义一个对象。
  • 为了方便其它程序对自定义对象的访问,可以对外提供一些访问方式。

饿汉式

对象一进内存直接创建对象。
Single类加载时,对象已经创建。

class Demo
{
	public static void main(String[] args){
		 Single ss= Single.getInstance(); //通过类名调用静态方法获取已经创建的对象的地址
		 ss.run();
	}

}

class Single
{
	private Single(){} //构造函数私有化
	private static Single s = new Single();//当类加载时创建好唯一对象,设为静态才能被静态方法调用

	public static Single getInstance(){//创建可调用的方法返回对象的地址
		return s; 
	}
	public static void run(){
		System.out.println("hh");
	}
}

懒汉式

对象是方法被调用时,才初始化,也叫做对象的延时加载。
single类进内存,对象还没有创建,只有调用了getInstance方法时,才创建对象。

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

	public static Single getInstance(){
		if(s==null){
			synchronized(Single.class){ 
			/*
			为了避免由于存在多个程序同时调用函数时导致CPU生成多个对象,用synchronized内置锁避免多
			个程序内逐一满足条件后再挨个执行创建对象的情况。
			*/
				if(s==null)				
					s=new Single();
				}
		}
		return s;
	}
	public static void run(){
		System.out.println("hh");
	}
}

总的来说,使用单例模式最终仍会用到对象的调用,所以对象一定会创建,所以饿汉式对于资源占用的较小,优先被使用。

你可能感兴趣的:(Java,java)