Java面对对象

问题:将大象放进冰箱需要几步?
面对过程:
1.打开冰箱门
2.将大象放进冰箱
3,带上冰箱门
面对对象
请一个人(:对象 new对象.方法(可以将大象放进冰箱的方法);帮我完成任务(将大象放进冰箱))

什么是类?

类定义了一种抽象数据类型

什么是抽象数据类型?

所谓抽象数据类型可以理解为:将不同的数据集合组成一个整体用来描述一种新的事物
类不但定义了抽象数据类型的组成(成员变量),同时还定义了可以对该类实施的操作(成员方法)

手机 = = >类
椅子 = = >类

小明的手机 = = >是手机类中的特定的某个手机 = = >称为该类中的具体某个对象 = = > 对象

定义一个类

  1. 定义类的成员变量
    类的定义包括"成员变量"的定义和"成员方法"的定义
    其中"成员变量"用于描述该类型对象共同的数据结构

    Java语言中,类的成员变量的定义可以使用如下方法:
    class 类名{ 成员变量类型 变量名称; }
    对象创建后,其成员变量可以按照默认方式初始化.

成员变量类型默认初始值
数值类型byte,short,int,long,float,double0.0
booleanfalse
char/u0000
引用类型null
  1. 定义类的方法
    类中处理定义成员变量,还可以定义方法,用于描述对象的行为,封装对象的功能
    Java语言中,可以按照如下的方式定义类中的方法

    class	类名{
    	修饰词 返回值类型 方法名称([参数列表]){
    		方法体;
    	}
    }
    

对象

对象即为类中的具体某个存在.

  1. 创建并使用对象
    使用new关键字创建对象
    类定义完成后,可以使用new关键字创建对象,创建对象通常称为实例化
    	int a = 1;//初始化
    	引用数据类型的初始化 = 实例化
    	new 类名();
    	JFrame frame = new JFarme();//创建一个窗体类
    	frame.setVisible(true);
    

引用类型变量

  • 为了能够对实例化的对象进行访问控制,需要使用一个特殊的变量------引用

  • 引用数据变量可以存放该对象的地址信息,通常称为"指向该类的对象";

  • 当一个引用类型变量指向该类的对象时,就可以通过这个变量对对象实施访问

  • 除了8个基本类型之外,用类接口,数组等声明的变量都称为引用类型变量,简称"引用"

  • 引用类型不算数据类型

  • 访问对象的成员变量.,调用方法

  • 可以通过引用访问对象的成员变量或调用方法

引用类型变量的赋值

  • 引用类型变量存储的是对象的地址信息,相同类型的引用类型变量之间也可以相互赋值
  • 引用类型变量之间的赋值不会创建新的对象,但有可能使得两个以上的引用指向同一个对象

null和NullPointerException

对引用类型变量,可以对其赋值为null,null 的含义为"空",表示还没哟指向任何对象

	Emp emp = new Emp();
	emp = null;
	//引用emp中的值为null,表示没有指向任何对象
	System.out.println(emp.name);
	//当一个引用为null的时候
	//,如果通过引用访问对象成员的变量或者调用方法是不和逻辑的,
	//会发生NullPointerException异常

重载

Java语言中,允许多个方法名称相同,但参数列表不同,称之为方法的重载(overload)

什么叫方法的重载

  1. 一个类中多个方法的名称相同,但参数列表不同
  2. 参数列表不同体现在:参数类型,参数数量不同,参数顺序不同等
  3. 和返回值,修饰符,final修饰,以及抛出的异常没有关系

编译时根据签名绑定调用不同的方法,我们可以把重载的方法看成是完全不同的方法,只不过恰好方法名相同而已

构造方法

构造方法的语法结构

构造方法时在类中定义的方法,不同于其他方法,构造方法有如下两点原则:

  1. 构造方法的名称必须与类名相同
  2. 构造方法没有返回值,但也不能写void
    语法:
    [访问修饰符]类名(){
    	//构造方法体
    }
    
    通过构造方法初始化成员变量
    构造方法常用于实现对象成员变量的初始化

this关键字的作用

this关键字用在方法中,指向该方法的当前对象,简单来说,那个对象调用方法,this指的就是那个对象,严格来讲在方法中需要通过this关键字指明当前对象

为方便起见,在没有歧义的情况下可以省略this

在构造方法中,用来初始化成员变量的参数一般和成员变量取相同的名字
这样会有利于代码的可读性,但是此处就必须通过关键字this来区分成员变量和参数(不能省略this)

ps:

  1. 我们也可以在方法中通过this关键字调用成员方法
  2. 不能在普通方法中通过this()/this([参数列表])的形式取调用构造方法
  3. 我们可以在构造方法中通过this()/this([参数列表])的形式取调用构造方法,但必须该语句在第一位

默认的构造函数

  1. 任何一个类都必须含有构造函数
  2. 如果源程序中没有定义,编译器在编译时将其添加一个无参的空构函数(称之为"默认的构造函数");
  3. 当定义了构造函数方法后Java编译器不在添加默认构造方法(所以无论有没有用在写程序时建议也一个无参的空构函数)
  4. 构造方法可以重载,彼此之间可以通过this()/this([参数列表])放在第一位

数组

引用类型数组
数组是对象

Cell cell = new Cell();
int[] arr = new int[];

在Java中,数组属于引用数据类型
数组对象在堆中存储,数组变量属于引用数据类型,存储数组对象的地址信息,指向数组对象
数组的元素可以看成数组对象的成员变量(只不过类型全部相同)
在这里插入图片描述

引用数据类型

  1. 引用数据类型的声明
    数组元素可以是任意类型,当然也包括引用类型

    	Cell[] cell = new Cell[4];
    	int[] a = new int[4];
    

    ps:new Cell[4]实际是分配了4个空间用于存放4个Cell类的引用,并不是分配了4个Cell类型对象

    在这里插入图片描述

  2. 引用数据类型的初始化

    	int[] a = new int[4];
    	Cell[] cell = new Cell[4];
    	Cell[0] = null; 
    

    引用类型数组的初始值都是null
    如果希望每一个元素都指向具体对象,需要针对每一个数组元素进行new运算

    	cell[0] = new Cell();
    	cell[1] = new Cell();
    	cell[2] = new Cell();
    	cell[3] = new Cell();
    

对象内存管理

编译好的Java程序需要在JVM中运行
程序,无论是代码还是数据,都需要存在内存当中,JVM为Java程序提供并管理所需要的空间
JVM内存分为"堆","栈"和"方法区"是三个区域,分别用于存储不同的数据

在这里插入图片描述

堆内存

对象存储在堆中
JVM在其内存空间中开辟一个称为"堆"的存储空间
这部分空间用于存储使用new关键字所创建的对象

在这里插入图片描述

成员变量的生命周期

访问对象需要依靠引用变量
当一个对象没有任何引用时,被视为废弃对象,属于被回收的范围,该对象中所有成员变量也随之被回收

	Cell c = new Cell();
	c = null;//不再执行刚才分配的对象空间,成员变量失效

成员变量的声明周期为:从对象在堆中创建开始到对象从堆中被回收结束

垃圾回收机制

垃圾回收机制(Garbage Collection, GC)是JVM自带的一个(守护)线程(自动运行着的程序),用于回收没有任何引用指向的对象
Java程序员不用担心内存管理,因为垃圾收集器会进行回收管理

Java程序内存泄漏问题

内存泄漏是指,不在使用的内存没有被及时回收,严重的内存泄漏会因为过多的内存占用而导致程序的崩溃.
因此当确定该对象不再使用时,应该及时将其引用设置为null

System.gc()方法

GC的回收对程序员来说是透明的,并不一定是一发现有无引用的对象就立刻回收.
一般情况下,当我们需要GC线程即刻回收废弃对象时,可以调用System.gc()方法
System.gc()方法用于建议虚拟机马上调用GC线程回收程序资源,具体的实现策略取决于JVM系统

栈用于存放方法中的局部变量
JVM在其内存空间中开辟一个称为"栈"的存储空间;
这部分空间用于存储程序运行时在方法中声明的所有局部变量.
在这里插入图片描述

局部变量的生命周期

一个运行的Java程序从开始到结束会有多次的方法的调用
JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧
一个栈帧对应一个正在调用中方法,栈帧中存储了该方法的参数,局部变量等数据.
当某一个方法调用完成后,对应的栈帧将被清除,局部变量失效.

	public void sum(int a,int b){
		int c = 0;
	}

在这里插入图片描述

ps:
方法只有一份
当类的信息被加载到方法区中时,除了类的类型和信息以外,同时类内的方法定义也被加载到方法区;
类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义>的.

	JFrame f1 = new JFrame();
	f1.setVisible(true);
	f1.setSize(200,300);
	
	JFrame f2 = new JFrame();
	f2.setVisible(true);
	f2.setSize(600,800);
	setSize方法只有一份,分别针对f1指向的对象和f2指向的对象调用了两次 

在这里插入图片描述

继承

泛化的过程
在这里插入图片描述

extends关键字

  1. 通过extends关键字可以实现类的继承:

  2. 子类(Sub class)可以继承父类(Super class)的成员变量以及成员方法(不能继承父类的构造方法),同时也可以定义自己的成员变量和成员方法

  3. Java语言不支持多继承,一个类只有一个父类,但父类可以有多个子类

继承中的构造方法

  • 子类的构造方法中必须通过super关键字调用父类的构造方法,这样可以妥善的初始化继承自父类的成员变量

  • 如果子类的构造方法中,没有调用父类的构造方法,Java编译器会自动的加入对父类无参构造方法的调用(该父类没有无参的构造方法,会有编译错误);

ps:

  1. 编译器只能添加super(),调用父类和无参构造函数,如果需要调用父类的有参构造需要手动添加
  2. 每个构造方法中都会添加调用父类的无参构造函数的代码,一旦手动添加了调用方法,则编译器将不再添 加默认的,并且手动添加的调用方法必须放在构造函数的第一行
  3. super关键字不仅可以用在构造方法中,还可以用在子类的普通方法中,super可以不放在第一行
  4. 子类普通方法中不能通过super关键字调用父类的构造函数

父类的引用指向子类

一个子类的对象可以向上造型为父类的类型,即定义父类的引用指向子类的对象

重写

方法的重写

子类可以重写(覆盖)继承来自父类的方法,即方法名和参数列表与父类方法相同,但是方法的实现不同
当子类对象的重写方法被调用是(无论是通过子类引用调用,还是通过父类的引用调用),运行的都是子类重写后的版本

重写中使用super关键字

子类在重写父类方法时,可以通过super关键字调用父类的版本

方法的重写要遵循"两同两小一大"原则:

  • 两同:方法名相同,形参列表相同
  • 两小:子类方法的返回值类型应该比父类方法要小或者相等,子类方法声明抛出异常应该比父类方法声明变量抛出的异常更小或者相等.
  • 一大:子类的访问权限要比父类的访问权限更大或者相等

重写和重载的区别

重载和重写是完全不同的两个语法现象 ,区别如下:

  • 重载是指在一个类中定义多个方法名相同,但参数列表不同的方法,在编译时,根据参数的个数和类型来决定那个方法

  • 重写是指在子类中定义和父类完全相同的方法,在程序运行时,根据对象类型的不同(不是引用类型)而调用不同的版本

  • 重载遵循所谓"编译期"绑定,即在编译时根据参数类型变量的类型判断应该调用那个方法

  • 重写遵循所谓"运行期"绑定,即在运行的时候根据引用变量指向的实际对象类型调用方法

面对对象特征

  1. 封装:封装私有化属性,提供公有的方法访问
  2. 继承:子类继承父类的所有属性和方法但没有权限的无法访问,比如private,default属性和方法
  3. 多态:同一种事物在不同情况下有不同的表现形式.重写,重载,父类引用指向子类对象,可变参数都是多态的表现方式
  4. 抽象:把实现世界的某一类东西,提取出来,用程序代码表示

封装的意义:

将功能封装成方法,外界提供调用

外界.方法([参数列表])

方法的实现发生了改变,对外界有影响吗?
没有

外界.方法([参数列表])

  1. 对外提供可调用的,稳定的功能

  2. 封装容易变化的,具体的实现细节,外界不可以访问,这样的意义在于降低代码出错的可能性,便于维护

  3. 当内部的实现细节发生了改变时,只要保证对外的功能定义不变,其他的模块就不会因此受到牵连

访问控制

包的概念

package语句

  1. 定义类是需要指定类的名称,但如果仅仅将类名做为类的唯一标识,则不可避免的出现命名冲突问题,这会给组建复用以及团队间的合作造成很大的麻烦.

  2. Java语言中用包(package)的概念来解决命名冲突问题,在定义类的时候,除了定义类名称之外一般还需要指定一个包,定义包名的语法:packaege 包名;

  3. package 语句必须要写在Java源文件的最开始,在类定义之前
    类的全称:一旦使用package语句指定了包名,类的全称即为"包名.类名"
    不同的包中可以定义相同的类名

  4. 包名也有层次结构,在一个包中可以包含另一个包,可以按照如下方式书写:
    package 包名1.包名2.包名3…
    如果各个公司或开发组织的程序员都随心所欲的命名包名的话,仍然不能从根本上解决命名冲突问题,因此,在指定包名的应该按照一定的规范.
    例如:
    org.apache.commons.lang.StringUtils
    StringUtils:类名
    org.apache.commons.lang:是多层包名,含有如下:
    org.apache表示公司或组织的信息(这个是公司(或者组织)的域名反写)
    commons:表示项目的名称信息
    lang:表示项目某个模块的名称

import语句

访问一个类时需要使用该类的全称,但这样的书写方式过于繁琐

java.util.Scanner = new java.util.Scanner(System.in);

可以通过import语句对类名的全称进行声明

import语句的语法为:
import 类的全局限定名(即包名+类名)

通过import语句声明类的全称后,该源文件中就可以直接使用类名来访问

访问控制修饰符

public和private

  • private修饰成员变量和方法仅仅只能在本类中调用
  • public修饰的成员变量和方法可以在任何地方调用
  • public修饰内容是对外提供可以被调用的功能,需要相对稳定;
  • private修饰的是对内实现的封装,如果"公开"会增加维护成本.

protected和默认访问控制

  • 用protected修饰的成员变量和方法可以被子类以及同一个包中的类使用.
  • 默认访问控制即不写任何控制访问控制符,默认访问控制的成员变量和方法可以被同一包中的类调用

访问控制符修饰成员

修饰符本类同一个包中的类子类其他类
public可以访问可以访问可以访问可以访问
protected可以访问可以访问可以访问不能访问
默认可以访问可以访问不能访问不能访问
private可以访问不能访问不能访问不能访问

static和final

static关键字

static修饰成员变量

  • 用static修饰的成员变量不属于对象的数据结构

  • static变量属于类的变量,通常可以用类名来引用static成员

  • static变量和类的信息一起存储在方法区中,而不是在堆中

  • 一个类的static成员变量只有"一份".无论该类创建了多少对象

public class cat(){
	private int age;
	private static int kindOfCats;
	public Cat(int age){
		this.age = age;
		System.out.println(++kindOfCats);	
	}
}
Cat c1 = new Cat(2);
Cat c2 = new Cat(3);
System.out.println(Cat.kindOfCats);//2

在这里插入图片描述

static修饰方法

通常的方法都会涉及到具体对象的操作,这些方法在调用时,需要隐式传递对象的引用(this)

int d = c2(this).eatFish(3);

调用eatFish方法时,除了传递参数3以外,还隐式传递了c2作为参数,在方法中的this关键字即表示该参数

static修饰的方法则不需要针对某些对象进行操作,其运行结果仅仅与输入的参数有关,调用时直接用类名引用

public static int drinking(int pOfW){
	return pOfW + 3;
}
int pOfW  = Cat.drinking(3);
  • 该方法调用时,没有隐式的传递对象的引用,因此在static方法中不可以使用this关键字
  • 类中被static修饰的变量称为"类的变量",不属于成员变量
  • 静态方法中不能使用类中定义的成员变量.
  • 静态方法中没有指向该变量的引用地址,能够使用同样被static修饰的变量,使用时无需初始化,含默认值
  • 由于static在调用时没有具体的对象,因此在static方法中不能对非static成员(对象成员)进行访问
  • static方法作用在于提供一些"工具方法"和"工厂方法"等.

static块

static块,属于类的代码块,在类加载期间执行的代码块,只执行一次,可以用来在软件中加载静态资源

public class Foo{
	static{
		//只执行一次
		System.out.println("Load Foo.class");
	}
	public Foo(){
		System.out.println("Foo.f()");
	}
}
Foo foo = new Foo();
/*
	输出结果为
	Load Foo.class
	Foo.f()
*/

Foo类加载时,运行静态代码块,在创建对象之前.

final关键字

final修饰变量

final关键字修饰成员变量,意为不可改变,初始化之后不可再改变
final修饰成员变量有两种初始化方式

  1. 声明同时初始化
  2. 构造函数初始化

ps:
final关键字修饰的成员变和普通成员变量没有区别,仅仅只是不能修改
final关键字也可以修饰局部变量,使用之前初始化即可

final修饰方法

final关键字修饰方法,不可以被重写
使一个方法不能重写的意义在于:放置子类在定义新方法时造成"不经意"重写.

final修饰类

final关键字修饰的类不可以被继承
JDK中的一些基础库被定义为final
Math,String,Integer…
使一个类不能被继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成危害

static final 常量

static final 修饰成员变量,必须声明同时初始化,不可被改变
static final 常量会在编译期被替换

	public static final int NUM = 100;
	System.out.println(Emp.NUM);
	//代码编译时会替换System.out.println(100);
	//static final 常量 Foo.NUM会在编译期被替换为其常量值(100)
	//在运行Main类时,Emp类是不需要被载入的,从而减轻了内存压力.

抽象类和接口

什么叫抽象类

  • 由abstract修饰的方法称为抽象方法,抽象方法只有方法定义,没有方法体和实现,用一个分号结尾
  • 一个类中如果包含抽象方法,该类应该用abstract关键字进行声明为抽象类
  • 如果一个类继承了抽象方法,必须要重写其重写其抽象方法,重写后的方法不再是抽象方法
  • 子类如果不想重写父类的抽象方法,可以将子类也设置为抽象类

ps:

  1. 包含抽象方法的类一定是抽象类
  2. 抽象类当中不一定包含抽象方法,可以包含普通成员变量和普通成员方法
  3. 抽象类不可以实例化
  4. abstract和final关键字不可以同时修饰一个类.因为final关键字使得类不可以被继承,而abstract如果不可以被继承将没有任何意义.

抽象类的意义

  • 为其子类提供一个公共类型
  • 封装子类中重复的内容(成员变量和成员方法)
  • 定义抽象方法,子类虽然有不同的实现,但该方法的定义是一致的

接口

定义一个接口

接口可以看成是特殊的抽象类,即只包含抽象方法的抽象类
在这里插入图片描述

实现接口

  • 与继承不同,一个类可以实现多个接口,实现的接口直接使用逗号分隔,当然,该类需要实现这些接口中定义的所用方法

  • 一个类可以通过"implements"关键字"实现"接口.一个类实现某个接口后就必须实现该接口中定义的所有方法

  • 接口可以作为一种类型声明变量,一个接口类型的变量可以引用实现了该接口的类的对象;
    通过该变量可以调用该接口中定义的方法(具体的实现类提供了具体的方法实现)

接口的意义和抽象父类非常相似

接口的继承

接口间可以存在继承关系,一个接口可以通过extends关键字继承另外一个接口,子接口继承了父类接口中定义的所有方法

接口和抽象类的区别

  1. 抽象类继承于object,接口不继承object
  2. 抽象类有构造器,接口中没有构造器
  3. 抽象类中可以有普通成员变量和常量,接口中只能有常量,而且修饰符只能是public static final
  4. 抽象类中可以有抽象方法,也可以有普通方法,接口中只能有抽象方法而且修饰符只能是public abstract
  5. 抽象类中可以有final的方法,接口中不能有final的方法
  6. 抽象类只能单继承,但接口可以多实现
  7. 抽象类中可以有静态方法,并且一定是一个普通方法,可以继承给子类,接口不可以

我们该什么时候使用抽象类和接口?

在java开发中90%使用的都是接口,抽象类往往只是实现一个过渡。抽象类定义的抽象方法与接口定义的方法有点不同,抽象类定义的方法父类不要求子类强制覆写,接口定义的方法,其实现子类必须覆写。

具体情况具体分析,举例如下:

  1. 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。

  2. 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。

  3. 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

多态

a子类 --> 父接口 父接口–> a = new a 子类();
—>父类 父类 a =

多态的意义

  1. 一个类型的引用在指向不同对象时会有不同的实现

思诚员工 emp1 = new 思诚讲师();
思诚员工 emp2 = new 思诚职发();
思诚员工 emp3 = new 思诚助教();
emp1.完成工作();
emp2.完成工作();
在这里插入图片描述

  1. 同一对象造型成为不同类型时,会有不同功能

思诚讲师 cy = new 思诚讲师();
思诚企业技术顾问 consulatant = cy;
思诚技术图书作者 author = cy;

cy.培训学生();
Consulatnt.介绍职业生涯();
Author.编辑稿件();

子类 父类 父接口1 父接口2…
子类 a = new 子类(); a能 点出子类中定义的方法
父类 b = new 子类(); b只能点出父类中定义的方法
父接口1 c = new 子类() 只能点出父接口1中定义的方法

向上造型

一个类的对象可以向上造型的类型有:

  1. 父类的类型
  2. 其实现的接口类型

Java编译器根据类型检查调用方法是否匹配.

强制转型

  1. 可以通过强制转换将父类型变量转换为子类型变量,前提是该变量指向的对象确实是该子类类型
  2. 也可以通过强制转换将变量转换为某接口类型,前提是该变量指向的对象确实实现了该接口.

如果在强制转换过程中出现了违背上述两个前提,将会抛出ClassCastException异常.
在这里插入图片描述

instanceof关键字

在强制转换中,为了避免出现ClassCaseException,可以通过instanceof关键字判断某个引用指向的对象是否为指定类型

//System.out.println(a instanceof C);
if(){
	C c = (C)a;
}else{
	System.out.println("类型不对");
}																

内部类

定义成员内部类

一个类可以定义在另外一个类的内部,定义在类内部的类称之为Inner,其所在的类称之为Outer
Inner定义在Outer的内部,通常只服务于Outer,对外部不具备可见性,Inner可以直接调用Outer的成员属性及方法(包括私有的)
在这里插入图片描述

public class Outer{
	private int time;
	private Inner inner;

	//初始化成员变量
	//在这里创建内部类对象
	public Outer (int time){
		this.time = time;
		inner = new Inner();
		//内部类调用方法
		inner.timerInc();
	}
	public void printTime(){
		System.out.println(time);
	}
	public class Inner{
		public void timerInc(){
			time++;
		}
	}
}

创建内部类对象

一般情况下,Inner对象会在Outer对象中创建(通常这个类需要实现某个接口或者继承某个类),而且创建对象之后,这个类的价值也就不存在了,这个类可以不必命名,称之为匿名内部类

public class Main{
	public static void main(String[] args){
		Action action = new Action(){
			public void excute(){
				System.out.println("hello world");
			}	
		};
		action.excute();
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值