Java进阶之面向对象

本文详细介绍了Java面向对象编程的核心概念,包括类、对象、继承、多态、关键字如this、package等,以及object类的重要特性、设计模式如单例和模板方法。通过实例演示,一步步引导读者掌握面向对象编程实战技巧。
摘要由CSDN通过智能技术生成


前言

Java是面向对象的一门编程语言,学习面向对象是java的核心内容,学习这部分内容要“大处着眼,小处着手”

Java的核心是面向对象;
面向对象设计的核心是类的实例化;
类的核心是属性和方法;

== “万事万物皆对象”==
1.在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
>Scanner,String等
>文件:File
>网络资源:URL
2.涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。


一、Java面向对象学习的三条主线

1.Java类及类的成员:属性、方法、构造器;代码块、内部类
2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)
3.其它关键字:this、super、static、final、abstract、interface、package、import等

二、面向对象与面向过程

以“人把大象装进冰箱”为例阐述面向对象与面向过程的区别

1.面向过程–“蛋炒饭”

面向过程强调的是功能行为,以函数为最小单位,考虑怎么做。
(1)把冰箱门打开
(2)抬起大象,塞进冰箱
(3)把冰箱门关闭

2.面向对象–“盖饭”

强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。

*{
 * 		打开(冰箱){
 * 			冰箱.开开();
 * 		}
 * 
 * 		抬起(大象){
 * 			大象.进入(冰箱);
 * 		}
 * 
 * 		关闭(冰箱){
 * 			冰箱.闭合();
 * 		}
 * 
 * }
 * 
 * 
 * 冰箱{
 * 		开开(){}
 * 		闭合(){}
 * }
 * 
 * 大象{
 * 		进入(冰箱){
 * 		}
 * }

三、面向对象的两个要素

1.类:对一类事物的描述,是抽象的、概念上的定义(抽象概念的人总体)

面向对象程序设计的重点是类的设计
设计类,就是设计类的成员。

2.对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)(具体的实实在在的某个人)

四、面向对象设计的编程

1.类的实例化

(1)设计类,其实就是设计类的成员
属性 = 成员变量 = field = 域、字段
方法 = 成员方法 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类

(2)类和对象的使用(面向对象思想落地的实现):

  • step1:创建类,设计类的成员
  • step2:创建类的对象
  • step3:通过“对象.属性”或“对象.方法”调用对象的结构

(3)如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。

package com.atguigu.java;
/*
 * 类和对象的使用
 * 1.创建类,设计类的成员
 * 2.创建类的对象
 * 3.通过“对象.属性”或“对象.方法”调用对象的结构
 */

public class PersonTest {
	public static void main(String[] args){
		//第二步:创建类的对象,即类的实例化
		Person p1 = new Person();
		//Scanner scanner = new Scanner(System.in);
		
		//第三步:调用对象的结构:属性和方法
		//调用属性:“对象.属性”
		p1.name = "zhangxiao";
		p1.age = 24;
		p1.isMale = true;
		System.out.println("您所创建的对象的名字是:"+p1.name);
		System.out.println("您所创建的对象的年龄是:"+p1.age);
		System.out.println("您所创建的对象的性别是男生吗?"+p1.isMale);
		//调用方法:“对象.方法”
		p1.eat();
		p1.sleep();
		p1.talk("chinese");
		
		Person p3 = p1;//将p1变量保存的对象地址值赋给p3,导致p1和p3指向了堆空间中的同一个对象实体。
		System.out.println(p3.name);//Tom	
		p3.age = 10;
		System.out.println(p1.age);//10,修改p3则p1也作相同的变化
	}
	

}

//第一步:创建类
class Person{
	//属性(变量)
	String name;
	int age = 1;
	boolean isMale;
	
	//方法(函数)
	public void eat(){
		System.out.println("人可以吃饭");
	}
	
	public void sleep(){
		System.out.println("人可以睡觉");
	}
	
	public void talk(String language){
		System.out.println("人可以说话,使用的是:"+language);
	}
}

2.对象的内存解析

在这里插入图片描述

3.属性

属性

成员变量
直接定义在类中的一对{}内
加载到堆空间中 (非static)

局部变量

声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
加载到栈空间

在这里插入图片描述

package com.atguigu.java;
/*
 * 类中属性的使用
 * 
 * 属性(成员变量)   vs  局部变量
 * 1.相同点:
 * 		1.1  定义变量的格式:数据类型  变量名 = 变量值
 * 		1.2 先声明,后使用
 * 		1.3 变量都有其对应的作用域 
 * 
 * 
 * 2.不同点:
 * 		2.1 在类中声明的位置的不同
 * 		属性:直接定义在类的一对{}内
 * 		局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
 * 		
 * 		2.2 关于权限修饰符的不同
 * 		属性:可以在声明属性时,指明其权限,使用权限修饰符。
 * 			常用的权限修饰符:private、public、缺省、protected  --->封装性
 * 			目前,大家声明属性时,都使用缺省就可以了。
 * 		局部变量:不可以使用权限修饰符。
 * 
 * 		2.3 默认初始化值的情况:
 * 		属性:类的属性,根据其类型,都有默认初始化值。
 * 			整型(byte、short、int、long):0
 * 			浮点型(float、double):0.0
 * 			字符型(char):0  (或'\u0000')
 * 			布尔型(boolean):false
 * 
 * 			引用数据类型(类、数组、接口):null
 * 
 * 		局部变量:没有默认初始化值。
 *  		意味着,我们在调用局部变量之前,一定要显式赋值。
 * 			特别地:形参在调用时,我们赋值即可。
 * 
 * 		2.4 在内存中加载的位置:
 * 		属性:加载到堆空间中   (非static)
 * 		局部变量:加载到栈空间
 * 
 */
public class UserTest {
	
	public static void main(String[] args) {
		User u1 = new User();
		System.out.println(u1.name);
		System.out.println(u1.age);
		System.out.println(u1.isMale);
		
		u1.talk("韩语");
		
		u1.eat();
		
	}
}

class User{
	//属性(或成员变量)
	String name;
	public int age;
	boolean isMale;
	
	
	public void talk(String language){//language:形参,也是局部变量
		System.out.println("我们使用" + language + "进行交流");
		
	}
	
	public void eat(){
		String food = "烙饼";//局部变量
		System.out.println("北方人喜欢吃:" + food);
	}
	
}

4.类中方法的声明和使用

方法中不可以再定义方法
但是方法中可以再调用方法
特别的,方法A调用方法A的过程叫做:递归

package com.atguigu.java;
/*
 * 类中方法的声明和使用
 * 
 * 方法:描述类应该具有的功能。
 * 比如:Math类:sqrt()\random() \...
 *     Scanner类:nextXxx() ...
 *     Arrays类:sort() \ binarySearch() \ toString() \ equals() \ ...
 * 
 * 1.举例:
 * public void eat(){}
 * public void sleep(int hour){}
 * public String getName(){}
 * public String getNation(String nation){}
 * 
 * 2. 方法的声明:权限修饰符  返回值类型  方法名(形参列表){
 * 					方法体
 * 			  }
 *   注意:static、final、abstract 来修饰的方法,后面再讲。
 *   
 * 3. 说明:
 * 		3.1 关于权限修饰符:默认方法的权限修饰符先都使用public
 * 			Java规定的4种权限修饰符:private、public、缺省、protected  -->封装性再细说
 * 
 * 		3.2 返回值类型: 有返回值  vs 没有返回值
 * 			3.2.1  如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用
 *                return关键字来返回指定类型的变量或常量:“return 数据”。
 * 				  如果方法没有返回值,则方法声明时,使用void来表示。通常,没有返回值的方法中,就不需要
 *               使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思。
 * 
 * 			3.2.2 我们定义方法该不该有返回值?
 * 				① 题目要求
 * 				② 凭经验:具体问题具体分析
 * 
 *      3.3 方法名:属于标识符,遵循标识符的规则和规范,“见名知意”
 *      
 *      3.4 形参列表: 方法可以声明0个,1个,或多个形参。
 *         3.4.1 格式:数据类型1 形参1,数据类型2 形参2,...
 *         
 *         3.4.2 我们定义方法时,该不该定义形参?
 *         		① 题目要求
 *         		② 凭经验:具体问题具体分析
 *      
 *      3.5 方法体:方法功能的体现。 		
 * 
 *  4.return关键字的使用:
 *  	1.使用范围:使用在方法体中
 *  	2.作用:① 结束方法
 *            ② 针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。
 *      3.注意点:return关键字后面不可以声明执行语句。
 *      
 *  5. 方法的使用中,可以调用当前类的属性或方法
 *  		特殊的:方法A中又调用了方法A:递归方法。
 *     方法中,不可以定义方法。
 */
public class CustomerTest {
	public static void main(String[] args) {
		
		Customer cust1 = new Customer();
		
		cust1.eat();
		
		//测试形参是否需要设置的问题
//		int[] arr = new int[]{3,4,5,2,5};
//		cust1.sort();
		
		cust1.sleep(8);
		
	}
}

//客户类
class Customer{
	
	//属性
	String name;
	int age;
	boolean isMale;
	
	//方法
	public void eat(){
		System.out.println("客户吃饭");
		return;
		//return后不可以声明表达式
//		System.out.println("hello");
	}
	
	public void sleep(int hour){
		System.out.println("休息了" + hour + "个小时");
		
		eat();
//		sleep(10);
	}
	
	public String getName(){
		
		if(age > 18){
			return name;
			
		}else{
			return "Tom";
		}
	}
	
	public String getNation(String nation){
		String info = "我的国籍是:" + nation;
		return info;
	}
	
	//体会形参是否需要设置的问题
//	public void sort(int[] arr){
//		
//	}
//	public void sort(){
//		int[] arr = new int[]{3,4,5,2,5,63,2,5};
//		//。。。。
//	}
	
	public void info(){
		//错误的
//		public void swim(){
//			
//		}
		
	}
}

补充说明:Java中方法声明和调用的三种方式

方式一:按照面向对象编程思想,将[step1创建类]和[step2类的实例化]的过程在一个java文件中编写,“方法的声明”与“方法的调用”在两个类class中,这两个class在一个文件中

step1:创建类,设计类的成员
step2:创建类的对象
step3:通过“对象.属性”或“对象.方法”调用对象的结构

package com.atguigu.java;
/*
 * 类和对象的使用
 * 1.创建类,设计类的成员
 * 2.创建类的对象
 * 3.通过“对象.属性”或“对象.方法”调用对象的结构
 */

public class PersonTest {
	public static void main(String[] args){
		//第二步:创建类的对象,即类的实例化
		Person p1 = new Person();
		//Scanner scanner = new Scanner(System.in);
		
		//第三步:调用对象的结构:属性和方法
		//调用属性:“对象.属性”
		p1.name = "zhangxiao";
		p1.age = 24;
		p1.isMale = true;
		System.out.println("您所创建的对象的名字是:"+p1.name);
		System.out.println("您所创建的对象的年龄是:"+p1.age);
		System.out.println("您所创建的对象的性别是男生吗?"+p1.isMale);
		//调用方法:“对象.方法”
		p1.eat();
		p1.sleep();
		p1.talk("chinese");
		
		Person p3 = p1;//将p1变量保存的对象地址值赋给p3,导致p1和p3指向了堆空间中的同一个对象实体。
		System.out.println(p3.name);//Tom	
		p3.age = 10;
		System.out.println(p1.age);//10,修改p3则p1也作相同的变化
	}
	

}

//第一步:创建类
class Person{
	//属性(变量)
	String name;
	int age = 1;
	boolean isMale;
	
	//方法(函数)
	public void eat(){
		System.out.println("人可以吃饭");
	}
	
	public void sleep(){
		System.out.println("人可以睡觉");
	}
	
	public void talk(String language){
		System.out.println("人可以说话,使用的是:"+language);
	}
}

方式二:按照面向对象编程思想,将[step1创建类]和[step2类的实例化]的过程分别在两个java文件中编写,“方法的声明”与“方法的调用”在两个类class中,这两个类class在两个文件中
step1:创建类
测试类:step2.类的实例化+step3.调用类
方式三:“方法的声明”与“方法的调用”在一个类class中,其实与面向对象编程并没有区别

step1.声明方法,相当于面向对象中的step1.创建类
step2.类的实例化
step3.调用方法

package com.atguigu.exer;
/*
 * 3.1 编写程序,声明一个method方法,在方法中打印一个10*8 的*型矩形,在main方法中调用该方法。
 */
public class Exer3Test {
	public static void main(String[] args){
		Exer3Test test = new Exer3Test();//调用方法时,首先要进行类的实例化,即创建类的对象
		
		//3.1测试类,调用方法
		test.method();
		
	}
	
	//3.1方法的声明
	public void method(){
		for(int i = 0;i < 10;i++){
			for(int j=0;j<8;j++){
				System.out.print("* ");
			}
			System.out.println();
		}
	}

}

5.方法的重载(overload)

(1)定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。

“两同一不同”:同一个类、相同方法名
参数列表不同:参数个数不同,参数类型不同

(2) 举例:
Arrays类中重载的sort() / binarySearch()
(3)判断是否是重载:跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
(4) 在通过对象调用方法时,如何确定某一个指定的方法:
方法名 —> 参数列表

6.方法的重写(override / overwrite)

(1)重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作

(2)应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。

(3)重写的规定:

 	方法的声明: 权限修饰符  返回值类型  方法名(形参列表) throws 异常的类型{
 					//方法体
 			}

(4) 约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
③ 返回值类型:
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

7.可变个数形参的方法

package com.atguigu.java1;
/*
 * 可变个数形参的方法
 * 
 * 1.jdk 5.0新增的内容
 * 2.具体使用:
 *   2.1 可变个数形参的格式:数据类型 ... 变量名
 *   2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,。。。
 *   2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
 *   2.4 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
 *   2.5 可变个数形参在方法的形参中,必须声明在末尾
 * 	 2.6  可变个数形参在方法的形参中,最多只能声明一个可变形参。
 * 
 */
public class MethodArgsTest {
	
	public static void main(String[] args) {
		
		MethodArgsTest test = new MethodArgsTest();
		test.show(12);
//		test.show("hello");
//		test.show("hello","world");
//		test.show();
		
		test.show(new String[]{"AA","BB","CC"});
		
	}
	
	
	public void show(int i){
		
	}
	
	public void show(String s){
		System.out.println("show(String)");
	}
	
	public void show(String ... strs){
		System.out.println("show(String ... strs)");
		
		for(int i = 0;i < strs.length;i++){
			System.out.println(strs[i]);
		}
	}
	//不能与上一个方法同时存在
//	public void show(String[] strs){
//		
//	}
	
	//The variable argument type String of the method 
	//show must be the last parameter
//	public void show(String ...strs,int i){
//		
//	}
	
}

8.变量的赋值

如果变量是基本数据类型,此时赋值的是变量所保存的数据值(并非地址),对新变量操作不会改变旧变量;
如果变量是引用数据类型(比如数组),此时赋值的是变量所保存的数据的地址值,对新变量操作会改变旧变量;

9.值传递机制

(1)方法形参的传递机制:值传递
(2)形参:方法定义时,声明的小括号内的参数;
实参:方法调用时,实际传递给形参的数据
(3)值传递机制:
如果参数是基本数据类型,此时实参赋给形参的是实参存储的数据值(并非地址值),方法内形参交换变化不会改变实参;
如果变量是引用数据类型,此时实参赋给形参的是实参存储数据的地址值,方法内形参交换变化会改变实参;

10.构造器(constructor)

一、构造器的作用:
1.创建对象:new + 构造器
2.初始化对象的信息
二、说明:
1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2.定义构造器的格式:权限修饰符 类名(形参列表){}
3.一个类中定义的多个构造器,彼此构成重载
4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
5.一个类中,至少会有一个构造器。

11.代码块(或初始化块)

在这里插入图片描述

package com.atguigu.java3;
public class BlockTest {
	public static void main(String[] args) {
		
		String desc = Person.desc;
		System.out.println(desc);
		
		Person p1 = new Person();
		Person p2 = new Person();
		System.out.println(p1.age);
		
		Person.info();
	}
}


class Person{
	//属性
	String name;
	
	int age;

	static String desc = "我是一个人";
	
	//构造器
	public Person(){
		
	}
	public Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	
	//非static的代码块
	{
		System.out.println("hello, block - 2");
	}
	{
		System.out.println("hello, block - 1");
		//调用非静态结构
		age = 1;
		eat();
		//调用静态结构
		desc = "我是一个爱学习的人1";
		info();
	}
	//static的代码块
	static{
		System.out.println("hello,static block-2");
	}
	static{
		System.out.println("hello,static block-1");
		//调用静态结构
		desc = "我是一个爱学习的人";
		info();
		//不可以调用非静态结构
//		eat();
//		name = "Tom";
	}
	
	//方法
	public void eat(){
		System.out.println("吃饭");
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	public static void info(){
		System.out.println("我是一个快乐的人!");
	}
	
}

12.接口

在这里插入图片描述

package com.atguigu.java1;

public class InterfaceTest {
	public static void main(String[] args) {
		System.out.println(Flyable.MAX_SPEED);
		System.out.println(Flyable.MIN_SPEED);
//		Flyable.MIN_SPEED = 2;
		
		Plane plane = new Plane();
		plane.fly();
	}
}


interface Flyable{
	
	//全局常量
	public static final int MAX_SPEED = 7900;//第一宇宙速度
	int MIN_SPEED = 1;//省略了public static final
	
	//抽象方法
	public abstract void fly();
	//省略了public abstract
	void stop();
	
	
	//Interfaces cannot have constructors
//	public Flyable(){
//		
//	}
}

interface Attackable{
	
	void attack();
	
}

class Plane implements Flyable{

	@Override
	public void fly() {
		System.out.println("通过引擎起飞");
	}

	@Override
	public void stop() {
		System.out.println("驾驶员减速停止");
	}
	
}

abstract class Kite implements Flyable{

	@Override
	public void fly() {
		
	}
	
}

class Bullet extends Object implements Flyable,Attackable,CC{

	@Override
	public void attack() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void method1() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void method2() {
		// TODO Auto-generated method stub
		
	}
	
}
//************************************

interface AA{
	void method1();
}
interface BB{
	
	void method2();
}

interface CC extends AA,BB{
	
}

五、面向对象的特征

1.封装性

一、问题的引入:
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到
属性的数据类型和存储范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值
加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs())
同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private).
–>此时,针对于属性就体现了封装性。
二、封装性的体现:
我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
拓展:封装性的体现:① 如上 ② 不对外暴露的私有的方法 ③ 单例模式 …
三、封装性的体现,需要权限修饰符来配合。
1.Java规定的4种权限(从小到大排列):private、缺省(什么也不写)、protected 、public
2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
在这里插入图片描述

总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。

2.继承性

在这里插入图片描述

(1)为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)。可以理解为:“子类 is a 父类”

(2)语法规则
class Subclass extends SuperClass{ }
Subclass:子类、派生类
Superclass:父类、超类、基类
(2.1)体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
(2.2)子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
子类和父类的关系,不同于子集和集合的关系。

(3)作用
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。

(4)Java中关于继承性的规定:

  • 一个类可以被多个子类继承。
  • Java中类的单继承性:一个类只能有一个父类
  • 子父类是相对的概念。
  • 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
  • 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法

(5)如果我们没有显示的定义一个类的父类的话,则此类继承于java.lang.object类。
也就是说,所有的java类(除了java.lang.object之外)都直接或间接的继承于java.lang.object类。
意味着所有类都具有java.lang.object声明的功能。

3.多态性

(1)理解多态性:可以理解为一个事物的多种形态。
(2)何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
(3) 多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
(4)多态性的使用前提: ① 类的继承关系 ② 方法的重写
(5)对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
面试题:多态性是编译时行为还是运行时行为?通过编程证明。 答:运行时行为

package com.atguigu.java5;


import java.util.Random;

//面试题:多态是编译时行为还是运行时行为?
//证明如下:
class Animal  {
 
	protected void eat() {
		System.out.println("animal eat food");
	}
}

class Cat  extends Animal  {
 
	protected void eat() {
		System.out.println("cat eat fish");
	}
}

class Dog  extends Animal  {
 
	public void eat() {
		System.out.println("Dog eat bone");

	}

}

class Sheep  extends Animal  {
 

	public void eat() {
		System.out.println("Sheep eat grass");

	}

 
}

public class InterviewTest {

	public static Animal  getInstance(int key) {
		switch (key) {
		case 0:
			return new Cat ();
		case 1:
			return new Dog ();
		default:
			return new Sheep ();
		}

	}

	public static void main(String[] args) {
		int key = new Random().nextInt(3);

		System.out.println(key);

		Animal  animal = getInstance(key);
		
		animal.eat();
		 
	}

}

六、关键字

1.this

package com.atguigu.java2;
/*
 * this关键字的使用:
 * 1.this可以用来修饰:属性、方法、构造器
 * 2.this修饰属性和方法:表示当前对象,或当前正在创建的对象
 * 
 *  2.1在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象的属性或方法。
 *     但是,通常情况下,我们都选择省略"this."。
 *     特殊情况下,如果方法的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表明此变量是属性,而非形参。
 *  2.2在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。
 *     但是,通常情况下,我们都选择省略"this."。
 *     特殊情况下,如果构造器的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表明此变量是属性,而非形参。
 * 3.this调用构造器,可以减少冗余
 *  3.1我们可以使用"this(形参列表)"的方式,调用本类中指定的其它构造器
 *  3.2构造器中不能使用"this(形参)"的方式调用自己
 *  3.3规定"this(形参列表)"必须声明在当前构造器的首行
 *  3.4构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器
 */
public class PersonTest {
	public static void main(String[] args) {
		Person p1 = new Person();
		
		p1.setName("zhangxiao");
		System.out.println(p1.getName());
		
		p1.setAge(24);
		System.out.println(p1.getAge());
	
		
	}

}

class Person{
	private String name;
	private int age;
	public Person(){
		String info = "空参构造器";
		System.out.println(info);
	}
	public Person(String name){
		this();
		this.name = name;
		
	}
	public Person(int age){
		this();
		this.age = age;
		
	}
	
	public void setName(String name){
	    this(24);
		this.name = name;
	} 
	
	public String getName(){
		return this.name;
	}
	
	public void setAge(int age){
		this.age = age;
	}
	
	public int getAge(){
		return this.age;
	}
	public void eat(){
		System.out.println("人吃饭");
		this.study();
	}
	public void study(){
		System.out.println("人学习");
	}
}

2.package

package com.atguigu.java2;

import java.lang.reflect.Field;
import java.util.*;

import com.atguigu.exer4.Account;
import com.atguigu.exer4.Bank;
import com.atguigu.java2.java3.Dog;

import static java.lang.System.*;
import static java.lang.Math.*;

/*
 * 一、package关键字的使用
 * 1.为了更好的实现项目中类的管理,提供包的概念
 * 2.使用package声明类或接口所属的包,声明在源文件的首行
 * 3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
 * 4.每"."一次,就代表一层文件目录。
 * 
 * 补充:同一个包下,不能命名同名的接口、类。
 *     不同的包下,可以命名同名的接口、类。
 * 
 * 二、import关键字的使用
 * import:导入
 * 1. 在源文件中显式的使用import结构导入指定包下的类、接口
 * 2. 声明在包的声明和类的声明之间
 * 3. 如果需要导入多个结构,则并列写出即可
 * 4. 可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构
 * 5. 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
 * 6. 如果使用的类或接口是本包下定义的,则可以省略import结构
 * 7. 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示。
 * 8. 使用"xxx.*"方式表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
 * 
 * 9. import static:导入指定类或接口中的静态结构:属性或方法。 
 */
public class PackageImportTest {
	public static void main(String[] args) {
		
		String info = Arrays.toString(new int[]{1,2,3});
		
		Bank bank = new Bank();
		
		ArrayList list = new ArrayList();
		HashMap map = new HashMap();
		
		Scanner s = null;
		
		System.out.println("hello!");
		
		Person p = new Person();
		
		Account acct = new Account(1000);
		//全类名的方式显示
		com.atguigu.exer3.Account acct1 = new com.atguigu.exer3.Account(1000,2000,0.0123);
		
		Date date = new Date();
		java.sql.Date date1 = new java.sql.Date(5243523532535L);
		
		Dog dog = new Dog();
		
		Field field = null;
		
		out.println("hello");
		
		long num = round(123.434);
	}
}

3.super

(1)super理解为:父类的
(2)super可以用来调用:属性、方法、构造器
(3)super的使用:调用属性和方法

  • 3.1 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
  • 3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
  • 3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。

(4)super调用构造器

  • 4.1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
  • 4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
  • 4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
  • 4.4 在构造器的首行,没有显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
  • 4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器

4.instanceof

(1)a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
(2)使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。

package com.atguigu.java;

import java.util.Date;
public class PersonTest {
	public static void main(String[] args) {
		
		Person p1 = new Person();
		p1.eat();
		
		Man man = new Man();
		man.eat();
		man.age = 25;
		man.earnMoney();
		
		//*************************************************
		System.out.println("*******************");
		//对象的多态性:父类的引用指向子类的对象
		Person p2 = new Man();
//		Person p3 = new Woman();
		//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
		p2.eat();
		p2.walk();
		
		
//		p2.earnMoney();
		
		System.out.println(p2.id);//1001
		
		System.out.println("****************************");
		//不能调用子类所特有的方法、属性:编译时,p2是Person类型。
		p2.name = "Tom";
//		p2.earnMoney();
//		p2.isSmoking = true;
		//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
		//编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
		
		//如何才能调用子类特有的属性和方法?
		//向下转型:使用强制类型转换符。
		Man m1 = (Man)p2;
		m1.earnMoney();
		m1.isSmoking = true;
		
		//使用强转时,可能出现ClassCastException的异常。
//		Woman w1 = (Woman)p2;
//		w1.goShopping();

		if(p2 instanceof Woman){
			Woman w1 = (Woman)p2;
			w1.goShopping();
			System.out.println("******Woman******");
		}
		
		if(p2 instanceof Man){
			Man m2 = (Man)p2;
			m2.earnMoney();
			System.out.println("******Man******");
		}
		
		if(p2 instanceof Person){
			System.out.println("******Person******");
		}
		if(p2 instanceof Object){
			System.out.println("******Object******");
		}
		
//		if(p2 instanceof String){
//			
//		}
		

		
		
		
	}
}

5.static

(1)static:静态的
(2)static可以用来修饰:属性、方法、代码块、内部类
(3)使用static修饰属性:静态变量(或类变量)

  •  3.1 属性,按是否使用static修饰,又分为:静态属性  vs 非静态属性(实例变量)
    
  •     实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的
    
  •          非静态属性时,不会导致其他对象中同样的属性值的修改。
    
  •   静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致
    
  •          其他对象调用此静态变量时,是修改过了的。
    
  •  3.2 static修饰属性的其他说明:
    
  •  	① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
    
  •      ② 静态变量的加载要早于对象的创建。
    
  •      ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
    
  •      ④		类变量	实例变量
    
  •      类		yes		no
    
  •      对象		yes		yes
    
  •  3.3 静态属性举例:System.out; Math.PI;
    

(4)使用static修饰方法:静态方法

  •  ① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
    
  •  ②			静态方法	   非静态方法
    
  •      类		yes		       no
    
  •      对象	yes		       yes
    
  •  ③ 静态方法中,只能调用静态的方法或属性
    
  •    非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
    

(5) static注意点:

  • 5.1 在静态的方法内,不能使用this关键字、super关键字
  • 5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。

(6) 开发中,如何确定一个属性是否要声明为static的?

  •  > 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
    
  •  > 类中的常量也常常声明为static
    
  • 开发中,如何确定一个方法是否要声明为static的?
  •  > 操作静态属性的方法,通常设置为static的
    
  •  > 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections
    

6.final

在这里插入图片描述

7.abstract

在这里插入图片描述

package com.atguigu.java;
public class AbstractTest {
	public static void main(String[] args) {
		
		//一旦Person类抽象了,就不可实例化
//		Person p1 = new Person();
//		p1.eat();
		
	}
}

abstract class Creature{
	public abstract void breath();
}

abstract class Person extends Creature{
	String name;
	int age;
	
	public Person(){
		
	}
	public Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	
	//不是抽象方法:
//	public void eat(){
//		
//	}
	//抽象方法
	public abstract void eat();
	
	public void walk(){
		System.out.println("人走路");
	}
	
	
}


class Student extends Person{
	
	public Student(String name,int age){
		super(name,age);
	}
	public Student(){
	}
	
	public void eat(){
		System.out.println("学生多吃有营养的食物");
	}

	@Override
	public void breath() {
		System.out.println("学生应该呼吸新鲜的没有雾霾的空气");
	}
}

七、object类(java.lang.Object类)

1.基础知识点

(1)Object类是所有Java类的根父类
(2)如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
(3)Object类中的功能(属性、方法)就具有通用性。

    属性:无
    方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()   wait() 、 notify()、notifyAll()

(4)Object类只声明了一个空参的构造器

2.equals()

(1)回顾 == 的使用:

== :运算符

  1. 可以使用在基本数据类型变量和引用数据类型变量中
  2. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
    如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
    补充: == 符号使用时,必须保证符号左右两边的变量类型一致。

(2) equals()方法的使用:

1.是一个方法,而非运算符
2.只能适用于引用数据类型
3. Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是
两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们
就需要对Object类中的equals()进行重写.
重写的原则:比较两个对象的实体内容是否相同.

(3)equals()方法的重写

手动实现equals()的重写-

//重写的原则:比较两个对象的实体内容(即:name和age)是否相同
	//手动实现equals()的重写
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
            return true;
        }
		
		if(obj instanceof Customer){
			Customer cust = (Customer)obj;
			//比较两个对象的每个属性是否都相同
			return this.age == cust.age && this.name.equals(cust.name);
		}else{
			return false;	
		}
		
	}

自动生成equals()的重写-
点击菜单栏中的source选项
在这里插入图片描述
选择generate hashCode() and equals(),点击ok,即可自动生成equals()方法。
在这里插入图片描述

//自动生成的equals()
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Customer other = (Customer) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

3.toString

(1)当我们输出一个对象的引用时,实际上就是调用当前对象的toString()

(2) Object类中toString()的定义:

public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());//返回对象的地址值
}

(3)像String、Date、File、包装类等都重写了Object类中的toString()方法。
使得在调用对象的toString()时,返回"实体内容"信息

(4)自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
手动实现重写
快捷键:toString+Alt+/

	手动实现
	@Override
	public String toString() {
		return "Customer[name = " + name + ",age = " + age + "]"; 
	}

自动生成重写
点击source菜单,选择generate toString即可
在这里插入图片描述

4.基本数据类型、包装类、String之间类型转换

在这里插入图片描述

八、设计模式

1.单例设计模式

(1)饿汉式

package com.atguigu.java2;

public class SingleTest {
	Bank bank1 = Bank.getInstance();
	Bank bank2 = Bank.getInstance();

}

//饿汉式单例模型
class Bank{
	//1.私有化类的构造器
	private Bank(){
		
	}
	//2.内部创建类的对象,静态的
	private static Bank instance = new Bank();
	
	//3.提供公共的静态方法,返回类的对象
	public static Bank getInstance(){
		return instance;
	}
}

(2)懒汉式单例模式

package com.atguigu.java2;
/*
 * 单例模式的懒汉式实现
 * 
 */
public class SingleTest1 {
	public static void main(String[] args) {
		
		Order order1 = Order.getInstance();
		Order order2 = Order.getInstance();
		
		System.out.println(order1 == order2);
		
	}
}


class Order{
	
	//1.私有化类的构造器
	private Order(){
		
	}
	
	//2.声明当前类对象,没有初始化
	//4.此对象也必须声明为static的
	private static Order instance = null;
	
	//3.声明public、static的返回当前类对象的方法
	public static Order getInstance(){
		
		if(instance == null){
			
			instance = new Order();
			
		}
		return instance;
	}
	
}

2.模板方法的设计模式

在这里插入图片描述

package com.atguigu.java;
/*
 * 抽象类的应用:模板方法的设计模式
 * 
 */
public class TemplateTest {
	public static void main(String[] args) {
		
		SubTemplate t = new SubTemplate();
		
		t.spendTime();
	}
}

abstract class Template{
	
	//计算某段代码执行所需要花费的时间
	public void spendTime(){
		
		long start = System.currentTimeMillis();
		
		this.code();//不确定的部分、易变的部分
		
		long end = System.currentTimeMillis();
		
		System.out.println("花费的时间为:" + (end - start));
		
	}
	
	public abstract void code();
	
	
}

class SubTemplate extends Template{

	@Override
	public void code() {
		
		for(int i = 2;i <= 1000;i++){
			boolean isFlag = true;
			for(int j = 2;j <= Math.sqrt(i);j++){
				
				if(i % j == 0){
					isFlag = false;
					break;
				}
			}
			if(isFlag){
				System.out.println(i);
			}
		}

	}
	
}

总结

本文主要介绍Java中面向对象的知识及编程应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

远方上&肖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值