5、Java类成员、面向对象的基本特征

Java类成员、面向对象的基本特征

  • 1、类的成员一:属性
    • 1.1 属性的基本特性
      • 1.1.1 声明的位置:
      • 1.1.2 声明的格式:
      • 1.1.3 属性的特点
      • 1.1.4 如何为对象的属性赋值
      • 1.1.5 如何访问对象的属性值
    • 1.2 成员变量与局部变量的区别
    • 1.3 属性的私有化-权限修饰符
    • 1.4 对象的内存分析
    • 1.5 对象数组
      • 1.5.1 声明对象数组
      • 1.5.2 对象数组初始化
      • 1.5.3 对象数组的内存图
  • 2、类的成员二:方法
    • 2.1 方法的基本特性
      • 2.1.1 方法的概念
      • 2.1.2 使用的原则、特点
      • 2.1.3 声明和调用格式要求
      • 2.1.4 总结
    • 2.2 方法的参数传递机制
      • 2.2.1 基本数据类型:8种
      • 2.2.2 引用数据类型:类、接口、数组等
    • 2.3 方法重载
      • 2.3.1 概念:方法的重载(Overload):(个人理解实质上就是形参列表不一样)
      • 2.3.2 例如:
    • 2.4 命令行参数
      • 2.4.1 定义
      • 2.4.2 格式
    • 2.5 可变参数
      • 2.5.1 可变参数:某个方法调用时,实参的个数是可变的。
      • 2.5.2 要求
    • 2.6 Java基本特征之一:封装
      • 2.6.1 封装的目的:隐藏实现细节,为了安全,也为了使用者方便
      • 2.6.2 封装
      • 2.6.3 标准的JavaBean格式get/set的格式
  • 3、类的成员三:构造器
    • 3.1 构造器的作用:
    • 3.2 构造器的声明和调用
    • 3.3 特点
    • 3.4 this关键字
    • 3.5 包
      • 3.5.1 包的作用:
      • 3.5.2 如何声明包:表示当前源文件中的所有类都是该包下的。
      • 3.5.3 包名的规范:
      • 3.5.4 使用其他包的类
      • 3.5.5 导包语句:
      • 3.5.6 如何编译带包声明的源文件
    • 3.6 基本特征之二:继承
      • 3.6.1 什么情况下会用到继承?
      • 3.6.2、实现继承
      • 3.6.3 继承的特点
    • 3.7 super关键字
      • 3.7.1 super.属性
      • 3.7.2 super.方法
      • 3.7.3 super()或super(实参列表)
    • 3.8 重写Override
    • 3.9 基本特征之三:多态
      • 3.9.1 多态的表现形式
      • 3.9.2 出现对象的多态性的前提
      • 3.9.3 多态的应用
      • 3.9.4 多态的5个案例题
    • 3.10 向上转型与向下转型
      • 3.10.1 父子类之间的转换规则
      • 3.10.2 instanceof
    • 3.11 方法重载和方法重写特例
      • 3.11.1 出现了重写还是重载?
      • 3.11.1 重载的陷阱参数“int...”识别数组报错问题
      • 3.11.3 特殊的重写,int[]把int...特性覆盖或增强
  • 4、类的第四个成员:代码块
    • 4.1 非静态代码块&静态代码块
      • 4.1.1 代码块的声明
      • 4.1.2 代码块的作用
      • 4.1.3 代码块的执行
      • 4.1.4 代码块的案例题
    • 4.2 超级父类Object
      • 4.2.1 方法1-2:`boolean equals(Object obj)` 和 `int hashCode() `
      • 4.2.2 方法3-5:`Class getClass()` 和 `void finalize() ` 和 `String toString()`
    • 4.3 static
      • 4.3.1 属性
      • 4.3.2 方法
      • 4.3.3 代码块
      • 4.3.4 修饰内部类
    • 4.4 变量的分类
      • 4.4.1 成员变量
      • 4.4.2 局部变量
    • 4.5 final
    • 4.6 native
  • 5、类的第五个成员:内部类
    • 5.1 内部类概念
    • 5.2 内部类的形式
    • 5.3 内部类分类1-静态内部类
    • 5.5 内部类分类2-非静态内部类
    • 5.6 内部类分类3-局部内部类
    • 5.7 内部类分类4-匿名内部类
    • 5.8 抽象类
      • 5.8.1 什么情况下会用到抽象类
      • 5.8.2 如何声明抽象类:(包含的静态方法可以直接通过”类名.方法名“调用)
      • 5.8.3 抽象类的特点
      • 5.8.4 抽象类与非抽象类的不同?
      • 5.8.5 说明:抽象类可以包含类的所的成员
      • 5.8.6 abstract关键字
    • 5.9 接口
    • 5.10 接口与抽象类的区别
    • 5.11 类加载初始化过程
    • 5.11.1 案例1
    • 5.11.2 案例2
    • 5.11.3 案例3
    • 5.11.4 案例4

类的5个成员:属性、方法、构造器、代码块、内部类
Java的3大基本特性:封装、继承、多态
----封装:安全、简单方便、对使用者隐藏实现细节
----继承:代码复用、扩展,表示is-a的关系
----多态:提高代码灵活性,增强代码功能

1、类的成员一:属性

属性:类的数据描述特征

1.1 属性的基本特性

1.1.1 声明的位置:

在类中方法外

1.1.2 声明的格式:

【修饰符】 数据类型 属性名;

1.1.3 属性的特点

(1)当创建对象后,没为属性赋值,那么属性默认值和数组的元素的默认值是一样的
byte,short,int,long:0
float,double:0.0
char:\u0000或说是Unicode编码为0的字符
boolean:false
引用数据类型:null
(2)每一个对象的属性在堆中是互相独立

1.1.4 如何为对象的属性赋值

1)显式初始化
class Circle{
	//显式初始化
	//每次创建圆的对象,半径的初始值都是1.0,而不是原来的0.0
	double radius = 1.0;
}2)手动赋值(其他类中)
对象名.属性 =;
	public static void main(String[] args){
		Circle c1 = new Circle();
		c1.radius = 2.0;//手动赋值
	}

1.1.5 如何访问对象的属性值

1)本类中
	★直接属性名
class Circle{
	double radius;
	double getArea(){
		return Math.PI * radius * radius;//直接访问
	}
}2)其他类中
	★对象名.属性名
	public static void main(String[] args){
		Circle c1 = new Circle();
		c1.radius = 2.0;
		System.out.println("圆的半径是:"  + c1.radius);
	}

1.2 成员变量与局部变量的区别

(1)声明的位置不同
局部变量:(1)方法体{}(2)形参(3)代码块中
成员变量:声明的位置,类中方法外:(1)类变量:static修饰(2)实例变量:没static修改
(2)存储的位置不同
局部变量:栈
成员变量:堆
(3)初始值的获取方式不同
局部变量:必须手动初始化
成员变量:如果没初始化,它默认值,也可以手动初始化(方式很多种)
(4)生命周期
局部变量:短,从代码运行到声明它的语句开始,到它的作用域结束就结束,每一个线程,每一次调用执行都是全新的生命周期
实例变量:随着对象的创建而出生,随着对象的回收而消亡,和对象同生共死
类变量:最长?
(5)作用域
局部变量:从声明处开始,到它所属的}结束
成员变量(实例变量、类变量)不谈作用域,在本类中,直接访问,其他类中用"对象."访问
(6)修饰符
局部变量:要么没,要么只能一个final
成员变量:可以没,如果可以很多…

1.3 属性的私有化-权限修饰符

一个类的属性,对外界想要控制访问,那么可以给属性设置修饰符,例如:private
权限修饰符/访问控制修饰符:可见的范围从小到大

范围 本类 本包 其他包子类 其他包非子类
private 可以 不可以 不可以 不可以
缺省 可以 可以 不可以 不可以
protected 可以 可以 可以 不可以
public 可以 可以 可以 可以

权限修饰符可以修饰:属性、方法、构造器、内部类
其中的public和缺省,还可以修改类

回忆:
属性的声明: 【修饰符】 数据类型 属性名;

类的声明: 【修饰符】 class 类名{ }

1.4 对象的内存分析

Java的内存分析图:
Java程序是运行在JVM中,JVM的功能之一:内存管理

(1)虚拟机栈,简称栈:存储局部变量
(2)堆:存储new出来的实例对象,包括数组
(3)方法区:用来存储不怎么变的东西,例如:加载的类的信息、静态的变量等
(4)本地方法栈:native等C语言实现的方法的变量等
(5)程序计数器:每一个线程当前运行到哪一个指令等信息
JVM高级时,还会再详细讲解,内存管理,以及垃圾回收的算法,内存优化

2、对象的内存结构图

对象的属性:存储在堆中
每一个对象的属性:每一个对象是独立的

5、Java类成员、面向对象的基本特征_第1张图片

5、Java类成员、面向对象的基本特征_第2张图片

1.5 对象数组

对象数组:它的元素数据类型是引用数据类型
例如:String[] ,Student[], Circle[]等

1.5.1 声明对象数组

元素的数据类型[] 数组名;

1.5.2 对象数组初始化

1、静态初始化
class TestObjectArray{
	public static void main(String[] args){
		//对象数组的静态初始化
		/*
		String[] array = {"hello","world","java"};
		
		//foreach遍历
		for(String str : array){
			System.out.println(str);
		}
		*/
		Circle[] array = {new Circle(),new Circle(),new Circle()};//这个圆对象是匿名对象
		//但是可以通过array[0],array[1],array[2]可以访问它,代表他,array[0],array[1],array[2]就好比是对象名
		
		//foreach遍历
		for(Circle c : array){
			System.out.println(c.radius);
		}
		
		//Circle c = new Circle();//不仅创建了一个对象,并且取名为c,可以称c为对象名或对象的引用
	}
}
class Circle{
	double radius;
}

2、动态初始化
(1)先指定数组的长度
(2)为元素赋值为一个对象
class TestObjectArray2{
	public static void main(String[] args){
		//1、声明数组
		Circle[] array;//元素的数据类型是Circle,说明该数组中只能存储圆对象,不能存别的
		
		//2、动态初始化
		//(1)指明数组的长度:要说明一共可以存几个元素,圆对象
		array = new Circle[3];
		
		//此时元素都是null
		
		//(2)为元素赋值,此处是赋值为一个圆对象
		array[0] = new Circle();
	}
}
class Circle{
	double radius;
}

1.5.3 对象数组的内存图

5、Java类成员、面向对象的基本特征_第3张图片
5、Java类成员、面向对象的基本特征_第4张图片

2、类的成员二:方法

2.1 方法的基本特性

2.1.1 方法的概念

方法(Method,又称为函数(Function,表示一个独立的功能。
在Java中作为类的成员。

2.1.2 使用的原则、特点

1、先声明后使用(和变量、类等一样)
2、不调用不执行,调用一次执行一次
3、一个方法最多只能返回一个结果,如果有多个数据,那么需要容器装进起来

2.1.3 声明和调用格式要求

1、声明的格式;

在类中方法外:
	【修饰符】 返回值类型  方法名(【形参列表】【抛出的异常列表】{
		方法体:功能的实现代码
		【return 返回值;}

说明:
(1)方法名:第一个单词首字母小写,从第二个单词开始首字母大写;见名知意;
(2)返回值类型
	A:如果方法的功能完成后不需要给调用者返回结果,那么声明方法时,返回值类型的位置用void表示。
	B:如果方法的功能完成后需要给调用者返回结果,那么声明方法时,返回值类型的位置就不能用void,具体的类型由结果的类型决定。
	  	返回值的类型可以是任意类型。
	C:如果返回值类型的位置不是void,那么在方法体中必须保证一定有return 返回值;的语句。
   		而且不管当前方法从哪个分支结束,都需要有对应的return 返回值;语句。
	D:一个方法只能有一个返回值。如果需要返回多个值,那么就要用数组或集合等装起来作为一个整体的结果返回。
(3)形参列表
	A:如果当前方法的功能完成,需要调用者传递数据来辅助功能的完成,那么就通过形参体现出来,否则就不需要声明形参。
	B:如果方法需要声明形参,那么格式是(数据类型 形参名,数据类型 形参名 。。。。),每一个形参用,分割,且都要有数据类型
	C:形参就是一个方法的局部变量,而且声明时值是不确定的,是要在调用时有实参类决定
	D:如果某个形参的值的个数不确定,可以声明为可变参数

2、调用格式

1)本类中
	 直接使用:方法名(【实参列表】)2)其他类中
	必须通过对象:对象.方法名(【实参列表】)

说明:
(1)如果被调用的方法的返回值类型位置是void
		那么该调用语句就必须单独成一个语句。
(2)如果被调用的方法的返回值类型位置不是void
		那么如果调用者需要这个返回值,那么就用变量接收 或者 直接打印 或者 用这个返回值再参与其他计算。 
(3)如果被调用的方法的形参列表是空的()
		那么调用时不需要传实参
(4)如果被调用的方法的形参列表不是空的(形参列表)
		那么调用时,一定要传实参的,而且实参的类型(一致或兼容)、个数、顺序与形参列表要一一对应。

2.1.4 总结

(1)本类中直接用,其他类用对象.
(2)返回值就接收,无返回值就别接收
(3)形参就传实参,没形参就不传实参,实参的作用就是给形参传值
(4)返回值类型:如果是void,说明没有返回值
如果是其他的,说明有返回值

方法形式有四种:

1、无参无返回值
2、有参无返回值
3、无参有返回值
4、有参有返回值

2.2 方法的参数传递机制

方法的参数的传递:实参-->形参(值传递)
形参:声明/定义方法()中的参数,被调用方法的()中的参数
实参:调用方法()中的参数

2.2.1 基本数据类型:8种

`实参给形参传 数据值`
`形参对值的修改  不会  影响实参的值`

2.2.2 引用数据类型:类、接口、数组等

`实参给形参传 地址值`
`形参对象对属性的修改  会  影响实参对象的属性`(本质上它俩就是同一个对象)

(陷阱)当形参重新new对象了,那么和实参就没有关系了,形参在修改属性,和实参就没关系了
(特殊)String类型比较特殊,它的对象有不可变性,所有的改变都会产生新的String对象,类似这样的对象,还有包装类对象等

2.3 方法重载

2.3.1 概念:方法的重载(Overload):(个人理解实质上就是形参列表不一样)

在同一个类中,出现了方法名称相同,但是形参列表不同的两个或多个方法,称为方法的重载。
和返回值类型无关。

2.3.2 例如:

public int max(int a, int b){
	System.out.println("2个整数的最大值");
	return a > b ? a : b;
}
public int max(int a, int b, int c){
	System.out.println("三个整数的最大值");
	int max = a>b ? a : b;
	max = max > c ? max : c;
	return max;		
}
public double max(double a, double b){
	System.out.println("两个小数的最大值");
	return a > b ? a : b;
}

2.4 命令行参数

2.4.1 定义

给main()传递的实参,叫做命令行参数。

2.4.2 格式

1、命令行执行程序时中输入:java 主方法所在的类名  实参值1  实参值2  实参值3。。。。无穷无尽个
	注:通过命令行编译正常进行,运行时候在正常的“java 主方法所在的类名”后边加上+空格+实参值1+空格+实参值2+...可以无穷多个
2、编译的是main(String[] args)引用类型数组
class TestCommandParamExer{
	public static void main(String[] args){
		System.out.println("长度:" + args.length);
		for(int i=0; i<args.length; i++){
			System.out.println(args[i]);
		}
		//-------------求和-----------
		int sum = 0;
		for(int i=0; i<args.length; i++){
			//sum += args[i];//不兼容的类型: String无法转换为int
			sum += Integer.parseInt(args[i]);//Integer.parseInt(字符串)把字符串转成整数int
		}
		System.out.println("和:" + sum);
	}
}

2.5 可变参数

2.5.1 可变参数:某个方法调用时,实参的个数是可变的。

格式:(数据类型... 可变参数名)

2.5.2 要求

(1)一个方法只能有一个可变参数
(2)可变参数必须在形参列表的最后

3、如何使用可变参数

(1)在声明它的方法中:当做数组使用
(2)可变参数对应的实参,个数可以是0-n个,但是类型必须对应
(3)特殊的情况:可变参数对应的实参,可以是一个对应类型的数组

例:

public static int sum_num(int... num) {
    int su = 0;
    for (int i = 0; i < num.length; i++) {
        su += num[i];
    }
    return su;
}

2.6 Java基本特征之一:封装

2.6.1 封装的目的:隐藏实现细节,为了安全,也为了使用者方便

2.6.2 封装

(1)属性的封装

一般情况下:
权限修饰符:private私有化
提供标准的get/set方法

(2)方法的封装:封装的是一个独立的功能,一段代码
(3)类的封装
(4)组件的封装:例如使用第三方的组件,支付宝,网银支付,对方给你开放了接口,你按照它规定的格式,把数据传给它,它计算完,把结果返回。
(5)系统的封装:对于使用者来说,不知道实现细节,但是知道怎么用。

2.6.3 标准的JavaBean格式get/set的格式

1)get方法:
【修饰符】 属性的类型 get属性名首字母大写(){
	return 属性;
}2)set方法:
【修饰符】void set属性名首字母大写(属性的类型 形参名){
	....
	this.属性 = 形参名;
}3)特别的是Boolean类型的属性,is属性名(属性的类型 形参名)
(4)最好保留无参构造

3、类的成员三:构造器

3.1 构造器的作用:

(1)和new一起使用时,用来创建对象:
new新对象时意思就是在运行这个构造器
new 构造器名(参数列表)
(2)构造器可以为属性赋初始值

3.2 构造器的声明和调用

1、构造器的声明:位置:类中,方法外
	语法格式:
		//无参构造
		【修饰符】 构造器名(){
		}
		//有参构造
		【修饰符】 构造器名(形参列表){
		}
	说明:构造器没有返回值类型,也不写void	

2、如何调用构造器:1)在非本类构造器中
		调用无参构造:
			new 构造器名()
		调用有参构造:
			new 构造器名(实参列表)2)在本类的构造器中?(留着)
	(3)在子类的构造器中?(留着)	

3、所以,创建对象的格式:
	之前:
	类名 对象名 = new 类名();
	现在:
	类名 对象名 = new 构造器名();//无参构造
	类名 对象名 = new 构造器名(实参列表);//有参构造

3.3 特点

(1)构造器的名称必须与所在的类名完全相同
(2)所有的类都有构造器,如果一个类没有显式声明构造器,
		那么这个类在编译时,将自动生成一个无参的构造器。
		如果显式声明了构造器,那么编译器将不再添加默认的无参构造。
(3)构造器可以重载
(4)构造器没返回值类型
(5)构造函数的修饰符只能是权限修饰符(public、缺省、protect、private)
        其他的(static、final、abstract)都不行

3.4 this关键字

this的意思:当前对象,本类的调用
(1)构造器中
    this当前对象表示的是正在new(创建)的那个对象
(2)成员方法中(非静态方法中)
    this当前对象表示的是调用该方法的对象
(3)静态方法中不能用this关键字

用法:

(1)this.属性 或 this.成员变量
当在某个方法中(构造器、成员方法),如果出现了局部变量(例如:形参和成员变量)属性重名了,
那么用“this.属性”进行区分。
如果该方法中不涉及到局部变量与属性重名问题,在属性的前面加不加this.都可以。
(2)this.方法
用this.方法表示访问当前对象的方法,完全可以省略this.
(3)this()this(实参列表)
用于调用本类的其他构造器,必须在构造器的首行。第一句。要避免递归调用。
this()用于表示调用本类的无参构造
this(实参列表)用于表示调用本类的有参构造

3.5 包

3.5.1 包的作用:

(1)避免类的重名
	有了包之后,类的全名称就变成了:包.类名
(2)管理类等资源文件
(3)顺便,控制某些类、成员的访问权限

3.5.2 如何声明包:表示当前源文件中的所有类都是该包下的。

格式:package 包名;
位置:必须在源文件的首行
包名,全部单词都小写,单词之间使用.分割,一般使用域名倒置 + 模块名

3.5.3 包名的规范:

(1)全部都小写,每一个单词之间使用.
(2)见名知意
(3)包名习惯上,用公司的域名倒置 + 模块名;
(4)包的修饰符

注:
公司的域名:www.meituan.com
常见项目中的包:
com.meituan.bean;
com.meituan.util;
com.meituan.dao;
com.meituan.service;

常见的一级二级域名:
com:商业公司
com.cn:中国地区的商业公司
edu:教育
org:组织,非盈利型
gov:政府

3.5.4 使用其他包的类

前提:被使用的类要具有可见性
    如果被使用的类的修饰符是缺省的,那么不能跨包使用
    如果被使用的类的修饰符是public的,那么可以跨包使用
同样:被使用的类的成员,也要注意“权限修饰符”的问题

如何使用:

(1)使用全名称
com.meituan.other.Teacher tea = new com.meituan.other.Teacher();
(2)导包语句 + 简名称
导包语句:
import com.meituan.other.Teacher;
代码中:
Teacher tea = new Teacher();

3.5.5 导包语句:

1、必须在package的后面,所有class声明的上面。
2importpackageclass之间,可以并列很多句。
形式:
(1import.类名;2import.*; 省略最后一级的类名,不能省略子包名
(3)静态导入:JDK1.5之后引入
		import static..静态成员名;
		import static..*;

当用到两个包的类,并且这两个类的名字一样:
其中一个用全名称,只有一个使用import或两个都用全名称

特别说明

同时使用名称相同,但是包名不同的两个类
例如:
	java.util.Date
    java.sql.Date

3.5.6 如何编译带包声明的源文件

(1)编译
javac -d . 源文件名.java
-d:创建包目录结构
.:在当前目录下创建包目录结构
(2)运行
java 包.类名

3.6 基本特征之二:继承

3.6.1 什么情况下会用到继承?

从代码的角度:
(1)自上而下:当设计一个新的类时,发现已经有一个类的所有属性,和方法,已经写好,而且正好也是我要的,可以继承它,再扩展它没有的部分
(2)自下而上:当发现设计了多个类时,这些类又一些共同的特征,那么可以把这些共同的特征(属性、方法)可以抽取到父类中,这样就不用多个类中重复编写。
目的:代码的复用、扩展
从逻辑角度的目的:用继承表示is-a的关系

3.6.2、实现继承

语法格式:
	【修饰符】 class  子类名  extends  父类名{
	}
子类(subclass):又称为派生类
父类(superclass):又称为超类、基类

3.6.3 继承的特点

  1. 子类会继承父类的所有的属性和方法,包括私有的,只不过私的不可以在子类中直接使用,因为不可见,但是可以间接使用到
  2. 子类不会继承父类的构造器
  3. 子类在构造器中一定会调用父类的构造器:
    • 默认情况下会调用父类的无参构造super(),而且是调用父亲然后父亲调用父亲,依此类推,打印时先打印辈分最大的最后打印自己的
    • 如果父类没无参构造,那么一定要在子类的构造器中用super(实参列表)手动调用父类的参构造
  4. 子类还可以扩展父类没有的属性、方法等。
  5. Java只支持单继承,但是支持多层继承。
    • 通俗的讲:Java类只能有一个直接的父类,但是可以有间接的多代的父类。
  6. Java中一个父类可以有很多个子类。

3.7 super关键字

super的意思:父类的调用
用法:
前提:对应的父类的属性或方法、构造器没私有化

3.7.1 super.属性

需要用:当子类声明了和父类非私有的同名的属性时,如果想要在子类中访问父类的这个属性时,那么需要加super.
设计者的角度应该避免
可用可不用的:当子类中没与父类非私有属性同名时,那么想要在子类中访问父类的属性时,那么可以加super.也可以不加

message:就近原则,访问方法里的局部变量。
this.message:局部变量与成员变量重名时,访问子类的成员变量属性。
super.message:不管重不重名都访问父类的变量属性。

3.7.2 super.方法

需要用:当子类重写了父类的某个方法时,在子类中又要调用父类被重写的方法时,那么需要加super.
可用可不用:当子类没重写父类的方法时,在子类中想要调用父类的方法,可以加super.也可以不加

3.7.3 super()或super(实参列表)

super()或super(实参列表)必须在子类构造器的首行
默认:super()	父类必须无参构造
手动:super(实参列表)  父类必须有有参构造,
	当父类没无参构造时,必须在子类的构造器的首行用这句话引用父类的有参构造。
	当父类有无参构造,如果你想要调用父类的有参构造,也可以使用这句话在子类构造器的首行调用。
补充说明:
this* this.属性,this.方法,他们的追溯不仅限于本类中,只要当前对象可访问都可以,
可以是本类声明的,也可以是从父类继承的。
* this()this(实参列表)必须是本类。
super* super()super(实参列表)必须是直接父类的。
* super.属性,super.方法,他们的追溯也不仅限于直接父类,可以间接父类的。
* 如果直接父类中找到了,就不往上了,如果没找到,会一直往上,直到找到为止。

3.8 重写Override

当父类的某个方法的方法体(方法的实现)不适合与子类时,那么子类应该选择重写该方法。

1、重写的要求

(1)两同:方法名与形参列表必须相同
(2)两小
返回值类型
------基本数据类型和void:子类重写的方法的返回值类型 与 父类被重写的方法的返回值类型 相同
------引用数据类型:子类重写的方法的返回值类型 <= 父类被重写的方法的返回值类型
------抛出的异常类型:同样是 <=
(3)一大:
权限修饰符:子类重写的方法的权限修饰符 >= 父类被重写的方法的权限修饰符
------ public > protected > 缺省(没写权限修饰符> private

2、哪些方法不能被子类重写?

(1)父类的private方法是否可以被重写?
------------不可以,因为父类的私有的方法,在子类中不可见
(2)父类的final方法不能被重写
(3)父类的static方法不能被重写

3、Overload、Override的区别?
Overload:方法的重载
Override:方法的重写

方法的重载 方法的重写
声明的位置 同一个类 父子类中
方法名 必须相同 必须相同
形参列表 必须不同 必须相同
返回值类型 无关 <=基本数据类型和void,必须相同;引用数据类型,可以“子类<=父类”
抛出的异常 无关 <=
权限修饰符 无关(建议一致) >= public > protect > 缺省 > private //“子类>=父类”

3.9 基本特征之三:多态

3.9.1 多态的表现形式

(1)方法的重载与重写
(2)对象的多态性,体现在编译时类型与运行时类型不一致,编译时看“父类”,运行时“看子类”,执行的是子类重写的方法体。

个人理解:
1、子类的new对象:可以赋给父类的变量
2、执行时候变量.方法调用子类方法
3、属性还是按照变量的方法使用

3.9.2 出现对象的多态性的前提

(1)继承
(2)重写:父类和子类有同名的方法。
(3)多态引用:父类的变量指向了子类的对象,或者说把子类的对象赋值给父类的变量,元素,形参等。

注意:多态性只有方法的重写重载和对象多态,属性没有多态性质,还是按照哪种类型的变量引用!

3.9.3 多态的应用

1、多态参数

形参是父类类型
实参是子类对象
Triangle t = new Triangle(3, 4, 5);//名对象,隐含  形参 =实参 Graphic g = t;
printArea(t);
public static void printArea(Graphic g){
    System.out.println(g.getArea());
}
// Triangle是Graphic的子类,他俩都有getArea方法,
// 用子类的对象赋给父类的变量来引用他们都有的求面积方法

2、多态数组

数组的元素类型是父类类型
数组的元素中存储的是子类的对象
例如:
	Fu[] arr = new Fu[3];
	//多态引用,左边的元素的类型是Fu类型,右边赋值的对象是子类的对象
	arr[0] = new Zi();
	arr[0].eat();//编译通过,因为arr[0]照Fu类型处理,Fu中eat方法
	//arr[0].smoke();//编译错误,因为arr[0]照Fu类型处理,Fu中没smoke方法

3.9.4 多态的5个案例题

--案例1
public class Exam1 {
	public static void main(String[] args) {
		A a1 = new A();//本态引用
		A a2 = new B();//多态引用
		B b = new B();//本态引用
		C c = new C();
		D d = new D();
		System.out.println("(1)" + a1.show(b)); 
		//因为B是D的父类,无法直接赋值,B是A的子类,
		// 所以可以把B的对象直接赋值给A的变量	
		System.out.println("(2)" + a2.show(d));
		//D是B的子类,B又是A的子类,但是B的对象个方法,
		//一个是show(D),一个是show(B),一个是show(A)	
		
		
		System.out.println("(3)" + b.show(c));
		//B的对象个方法,一个是show(D),一个是show(B),
		// 一个是show(A),因为C是B的子类	
		System.out.println("(4)" + b.show(d));
		//B的对象个方法,一个是show(D),一个是show(B),一个是show(A)	
//		D d2 = new B();
	}
}
class A{
	public String show(D obj){
		return ("A and D");
	}
	public String show(A obj){
		return "A and A";
	}
}
class B extends A{
	public String show(B obj){
		return "B and B";
	}
	public String show(A obj){
		return "B and A";
	}
}
class C extends B{}
class D extends B{}
// 答案:
//(1)A and A
//(2)A and D
//(3)B and B
//(4)A and D

--案例2
public class Exam2 {
	public static void main(String[] args) {
		Base b = new Sub();//多态引用
		System.out.println(b.x);
	}
}
class Base{int x = 1;}
class Sub extends Base{int x = 2;}
答案是:  1
//属性没编译时类型和运行时类型多态性
//方法是有多态引用

--案例3
public class Father{
	private String name = "atguigu";
	int age = 0;
}
public class Child extends Father{
	public String grade;
	public static void main(String[] args){
		Father f = new Child();
		System.out.println(f.name);
	}
}
//编译错误,因为父类中name是不可见的,子类对象不可直接调用

--案例4
public class Person{
	public Person(){
		System.out.println("this is a Person.")
	}
}
public class Teacher extends Person{
	private String name = "tom";
	public Teacher(){
		System.out.println("this is a teacher.");
		super();				               	//必须在子类构造器的首行
	}
	public static void main(String[] args){
		Teacher tea = new Teacher();
		System.out.println(this.name);         //静态方法中不能使用this关键字
	}
}

--案例5
public class Test {
	public static void main(String[] args) {
		Base b1 = new Base();//本态引用,创建父类的对象
		//base:100
		Base b2 = new Sub();//多态引用,创建子类的对象
		//sub : 100
		//base :70
	}
}
class Base{
	Base(){
		//this.method()  
		//构造器中的this,正在new的对象,现在是new子类的对象,执行子类重写的method
		method(100);
	}
	public void method(int i){
		System.out.println("base : " + i);
	}
}
class Sub extends Base{
	Sub(){
		//super();//这里隐藏了super()
		super.method(70);//执行的是父类的method
	}
	public void method(int j){
		System.out.println("sub : " + j);
	}
}
//答案:
base : 100
sub : 100
base : 70

3.10 向上转型与向下转型

3.10.1 父子类之间的转换规则

(1)向上转型(多态):子类 -> 父类
当子类的对象赋值给父类的变量、元素、形参等时,都会发生自动类型转换,向上转型
(2)向下转型: 父类–>子类
当把父类的变量赋值给子类的变量,一定要用强制类型转换符()
强制类型转换会风险:可能会报ClassCastException可以通过instanceof判断进行避免
只当父类的变量中存储的就是该子类或该子类的子类对象时,才能向下转型成功,否则都是失败。

这就是:		Person p = new Man();//多态引用,向上转型,
			//p.smoke();//因为在编译期间,子类的对象被向上转型成父类的类型
		
			Man m = (Man)p;//向下转型
			m.smoke();
			
			Person p2 = new Person();
			//Man m2 = (Man) p2;//失败,ClassCastException
			
			Person p3 = new Woman();
			//Man m3 = (Man) p3;//失败,ClassCastException

如何避免:	//如果p指向的是男人对象,那么调用一下它的smoke()
			if(p instanceof Man){
				Man m = (Man) p;
				m.smoke();
			}
			//如果p指向的是女人对象,那么调用一下它的shopping()
			if(p instanceof Woman){
				Woman w = (Woman) p;
				w.shopping();
			}

3.10.2 instanceof

if(【运行时是】子类对象 instanceof 子类类型){//true}
if(【运行时是】子类对象 instanceof 父类类型){//true}
if(【运行时是】父类对象 instanceof 子类类型){//false}
if(【运行时是】子类类型2的对象 instanceof 子类类型1){//false}

补充说明:instanceof只能用于本类对象或具父子类关系的对象判断,其他的类型是不能用的
if(【编译时是】子类对象 instanceof 子类类型){//编译通过}
if(【编译时是】子类对象 instanceof 父类类型){//编译通过}
if(【编译时是】父类对象 instanceof 子类类型){//编译通过}
if(【编译时是】子类类型2的对象 instanceof 子类类型1){//编译不通过}

3.11 方法重载和方法重写特例

3.11.1 出现了重写还是重载?

public class TestOverload {
	public static void main(String[] args) {
		Son son = new Son();
		son.test("哔站");
	}
}
class Father{
	public void test(){
		System.out.println("父类的无参无返回值的方法");
	}
}
class Son extends Father{
	//不是重写?是否是重载?非严格意义的重载.
	//和从父类继承的test()构成了像重载的形式,对于Son对象可以访问两种形式test()
	public void test(String info){
		System.out.println("子类的参无返回值的方法:" + info);
	}
}

3.11.1 重载的陷阱参数“int…”识别数组报错问题

// 2、重载的陷阱:为什么报错?因为int...,编译时,照数组处理的,只不过它比数组更灵活
public class TestOverload2 {
	public static void main(String[] args) {
		test();
		test(0);
		test(1,2,4);
		int[] arr = {3,4,5,6,7};
		test(arr);
	}
	//调用时,可以给0~n个int值
	//也可以给它一个int[]类型的实参
	public static void test(int... args){
		//省略代码
		for (int i = 0; i < args.length; i++) {	
		}
	}
	//调用时,必须给一个int[]类型的实参
//	public static void test(int[] args){
//		//省略代码
//	}
}

3.11.3 特殊的重写,int[]把int…特性覆盖或增强

// 1、特殊的重写,int[]把int...特性覆盖掉了
// (调用子类时就看子类形参列表是什么样的就是什么样的)
public class TestOverride {
	public static void main(String[] args) {
		Zi zi = new Zi();
//		zi.test(1,2,4);//报错,
	}
}
class Fu{
	public void test(int... args){
		//省略
	}
}
class Zi extends Fu{
	//特殊的重写,int[]把int...特性覆盖掉了
	@Override
	public void test(int[] args){
		//省略
	}
}

//2、特殊的重写,int...把int[]覆盖了,变得更强大了
// (调用子类时就看子类形参列表是什么样的就是什么样的)
public class TestOverride2 {
	public static void main(String[] args) {
		B b = new B();
		b.test(1,2,4);
	}
}
class A{
	public void test(int[] args){
		
	}
}
class B extends A{
	//int...把int[]覆盖了
	@Override
	public void test(int... args){
	}
}

4、类的第四个成员:代码块

声明的位置:类中方法外
声明的格式:
    类{
    	【static】{
    	}
    }
分类:
    静态代码块和非静态代码块

4.1 非静态代码块&静态代码块

4.1.1 代码块的声明

1、声明的位置:类中方法外
2、声明形式:
	(1)非静态代码块
		 {
		 	语句;
		 }2)静态代码块 
		 static{
		 	语句;
		 }

4.1.2 代码块的作用

为成员变量初始化。
1、非静态代码块为非静态成员变量,即实例变量初始化;
2、静态代码块为静态成员变量,即类变量初始化;

4.1.3 代码块的执行

A:每次创建对象时自动执行。
     本质上:Java编译器,会把我们的
         (1)实例变量的显式初始化
         (2)非静态代码块
         (3)构造器的代码
 B:合起来编译为一个(),实例初始化方法。
     特别说明:
         (1)显式初始化和
         (2)非静态代码块谁在上面谁先执行,构造器永远是最后执行
     并且()方法的执行是一起执行的,不会拆开。
 C:子类的方法执行会先导致父类的方法执行。

4.1.4 代码块的案例题

package com.atguigu.block;
/*
 * 子类对象的创建会导致父类的构造器被调用。
 * 子类实例化的过程会导致父类的实例化过程执行。
 * 子类一个(),父类也一个(),
 		那么子类的()被执行时,会导致父类的()方法被执行,
 * 原因是因为子类的构造器的首行,一句super()
 * 
 * Sub对象的创建:
 * (1)父类的()执行
 * (2)子类的()执行
 * 
 * 父类的()由什么组成:
 * (1) 实例变量的显示初始化:i = init();
 * (2)父类的非静态代码块
 * (3)父类的构造器
 * 
 * 子类的()由什么组成?
 * (1)实例变量的显示初始化:j = test();
 * (2)子类的非静态代码块
 * (3)子类的构造器
 */
public class TestInstance2 {
	public static void main(String[] args) {
		Sub s = new Sub();
		System.out.println("-------------------");
		Sub s2 = new Sub();
	}
}
class Base{
	private int i = init();//显式初始化
	public Base(){
		System.out.println("父类构造器:i = "  + i );
		i++;
	}
	{
		System.out.println("父类非静态代码块 i = " + i);
		i++;
	}
	public int init(){
		System.out.println("父类赋值之前i = " + i);
		i++;
		System.out.println("父类为i赋值的一个方法 i = " + i);
		return i;
	}
	public int getI() {
		return i;
	}
}
class Sub extends Base{
	private int j = test();//显式初始化
	public Sub(){
		System.out.println("子类构造器:j = "  + j );
		j++;
	}
	{
		System.out.println("子类非静态代码块 j = " + j);
		j++;
	}
	public int test(){
		System.out.println("子类赋值之前j = " + j);
		j++;
		System.out.println("子类为i赋值的一个方法 j = " + j);
		return j;
	}
	public int getJ() {
		return j;
	}
}

4.2 超级父类Object

1、java.lang.Object类是超级父类。它是所类的根父类。
	例如:数组类型:int[],String[],Customer[],int[][],String[][],包括数组
(1)它的所属性、方法都可以被所有类型继承。
			换句话说,所有子类(所有类)型都拥Object类中的方法
(2)它的无参构造一定会被调用
(3)Object类型变量、形参、元素可以接收任意类型的对象

4.2.1 方法1-2:boolean equals(Object obj)int hashCode()

1public boolean equals(Object obj):做比较
区别:==和equals
如果子类没重写equals()方法,那么Object默认的equals方法的实现,和“==”是一样的。
如果子类希望equals()比较的不是对象的地址,而是其他信息,
	例如:属性的值等,那么就要重写equals方法。
	例如:String等都是重写了equals方法
		默认是两个对象的地址
	子类可以重写,重写后一般比较属性的内容
(2public int hashCode():返回哈希值,例子用int接受的
hashCode()返回当前对象的哈希值
	这个值只是在当把对象放到类似于“哈希表”等容器中时才用,其他时候没用。
后面会学习把对象放到“HashtableHashMapHashSet等集合容器”中时,才会用到。

结论:
public boolean equals(Object obj) 
public int hashCode() 
形影不离,总是同时被重写。而且参与equals比较的属性,一定要参与hashCode值的计算。

两个方法想重写的话,快捷键就是Alt+shift+s选择重写他俩选择哪些些属性就了
	(1)如果两个对象的hashCode()值不相同,我们就断定这两个对象一定不相同:
			就可省略equals,肯定是false2)如果两个对象equals比较相等,那么这两个对象的hashCode值一定相同
	(3)如果两个对象的hashCode()值相同:
			那么我们调用equals比较的结果可能为true,也可能为false

4.2.2 方法3-5:Class getClass()void finalize() String toString()

3public Class getClass():返回某个对象的运行时类型(个人理解包名.属性/类)
		test("hello");
		test(new Student("张",23));
		public static void test(Object obj){
		//获取obj对象的运行时类型String输出结果	java.lang.String
		Class c = obj.getClass();
		//类型名称自己建的类名结果		com.atguigu.object.Student
		System.out.println(c.getName());4protected void finalize():这个方法是由GC(垃圾回收程序调用,不是由程序员调用
一、什么是垃圾?
	MyData my = new MyData();
	//for中的局部变量,每循环一次,上一次的my就失去生命了,右边的对象就是垃圾了
	
	Java的垃圾回收器的特点:它什么时候来回收,是不确定的。一般:1)内存吃紧了
		(2)通知它来回收
		(3System.gc();//这个通知垃圾回收器过来回收,实际时间是系统自己的安排

二、什么时候调用?什么样的代码会写在这个方法中?
	一般是资源对象会重写这个方法,然后会在这个方法中写彻底释放资源的代码。
	资源对象:IO流,数据库连接

三、这个方法一个特点?
	如果在finalize()方法中,使得这个对象“复活”了,
	(即在方法中,使得另一个有效变量指向当前对象this,那么当前对象this就“复活”)
	一旦“复活”垃圾回收器就不能回收他了,等他下次再次变成垃圾后,垃圾回收器,
	就不再第二次调用这个对象的finalize方法,直接回收。
	即每一个对象的finalize()只能被调用一次。
(5public String toString()1、默认返回的是:类名@哈希值的十六进制形式
	2、子类可以重写
	3、如果直接打印一个对象,或者是一个对象与字符串进行“+”拼接时:
		默认自动调用该对象的toString()

	(意思就是:
		1)把正常的getInfo方法换成快捷键生成toString方法
		2)然后调用打印时候直接打印对象就行,不用写“arr[i].getInfo”了
		3)直接写arr[i]就行,因为自动调用toString方法
	)

4.3 static

static:静态的(不能修饰外部类)
可以修饰:
    	内部类、
    	属性(称为静态变量、类变量)、
    	方法(称为静态方法、类方法)、
    	代码块(称为静态代码块)

4.3.1 属性

用static修饰的属性,称为静态变量,类变量。

特点:

(1)不管创建多少个该类的实例对象,静态变量只一份。即所有对象共享。存储在方法区。
(2)类变量是在类初始化时初始化。因此,比创建对象早,比实例变量初始化早。
 静态变量的get/set也是静态的,建议用"类名."调用,当然也可以用"对象."

4.3.2 方法

用static修饰的方法,称为类方法,静态方法;方法里只能访问static类型的变量属性,不能有非static类型的成员!

特点:
(1)可以用"类名."调用,不需要对象(不用创建对象,类名.方法就可以直接调用)。
例如:Arrays,Math
(2)static方法中,不能直接使用本类的非静态的属性、方法、this关键字、super关键字

4.3.3 代码块

用static修饰的代码块,称为静态代码块。
作用:为静态变量初始化

执行特点:

(1)在类初始化时执行,而且只执行一次。比创建对象要早,比调用方法要早。
(2)本质上,类初始化其实是一个方法(),叫类初始化方法。类的初始化只有一边,无论怎么调用都只初始化一遍
-------这个()方法的代码由:
--------------A:静态变量的显式初始化代码
--------------B:静态代码块代码
-------同样,A和B谁在上面谁先执行。(强调无静态构造器)
(3)子类的初始化会先检查父类是否初始化,如果父类没初始化,那么会先初始化父类。

4.3.4 修饰内部类

4.4 变量的分类

变量:
	本质上是代表内存的一块存储区域。
三个要素:
	(1)数据类型
	(2)变量名
	(3)变量值
变量的使用要注意:
	(1)先声明后使用
	(2)使用之前要初始化值
	(3)要注意注意域
变量的分类:成员变量 & 局部变量

4.4.1 成员变量

1)静态的成员变量:类变量,静态变量
(2)非静态的成员变量:实例变量,非静态变量

声明的位置:类中方法外
存储的位置:
(1)静态变量:方法区
(2)实例变量:堆
生命周期:
(1)静态变量:和类同生死共存亡
(2)实例变量:和对象的声明周期一样。
作用域:
(1)静态变量:本类中,直接用,其他类,可以通过对象.也可以通过类名.访问
(2)实例变量:本类中,直接用,其他类,通过对象.
赋值:
(1)静态变量:默认值、显式初始化或静态代码块、set方法
(2)实例变量:默认值、显式初始化或非静态代码块、构造器、set方法
修饰符:
(1)静态变量:权限修饰符、staticfinal2)实例变量:权限修饰符、final

4.4.2 局部变量

声明的位置:
(1)方法体{}中:最常见
(2)方法头()中:形参      方法头也称为方法签名。
(3)代码块中:很少见。

存储的位置:栈
生命周期:短   从执行到声明它的语句开始,到作用域就结束了。
				一般是说和方法调用的生命周期差不多。
作用域:小      从声明处开始,到所属的}结束

赋值:		
(1方法体{}:手动赋值   变量 = xx;
(2形参:调用方法时,由实参赋值
(3代码块:手动赋值   变量 = xx;	
修饰符:final

4.5 final

修饰符:final,最终的
可以修饰什么?属性、方法、内部类、外部类、局部变量
1、类:包括内部类和外部类
2、变量:成员变量(类变量、实例变量和局部变量
3、方法

一、修饰类

表示这个类不能由子类,即不能被继承。 ————太监类
例如:String,System,Math

二、修饰方法

表示这个方法不能被重写。
可以被继承不能被重写。

三、修饰变量

表示这个变量的值不能被修改,即常量。导致这个变量无set方法
修饰变量,那么必须为这个变量手动初始化,不管是局部变量还是成员变量。
若此方法未初始化那么一定要有参构造才能初始化,不能有无参构造

4.6 native

修饰符:native:本地的
可以修饰?方法
修饰的方法表示该方法的方法体不是由Java语言实现的,
由非Java语言,例如:C、c++等实现的,编译成.dll文件,由Java调用。

对于Java程序来说,调用native修饰的方法,和调用其他的Java方法一样规则。
 并且对于子类来说,也可以重写它。

5、类的第五个成员:内部类

Java的成员:
(1)属性( * * * * * )
(2)方法( * * * * * )
(3)构造器( * * * * * )
(4)代码块( * * )–》(1)面试(2)作用:为属性初始化,静态代码块为静态变量–》类初始化,非静态代码块为非静态变量–>实例初始化。
(5)内部类( * * )

5.1 内部类概念

1、概念:声明在其他类里面的类叫做内部类。我们把外面的这个类称为外部类。
2、回忆:
        类的概念:一类具有相同特性的事物的抽象描述。
        在描述一类事物时,发现它的内部也有一个独立的结构,也需要用一个类来描述。
        而且这个内部的事物只为外部类服务,或者是要依赖于外部类,所以我们把这样的内部结构声明为内部类。
        例如:身体Body,内部可能心脏Heart等

        内部类它是可以使用外部类的所的成员和数据,包括私的。所以我们把这个类声明为内部类更方便。

        现在内部类的使用的频率在JavaEE里面比较少,之前在JavaSE的GUI,Andriod等里面大量的使用内部类。
        但是在我们接下来要学习的集合框架类中,会很多的内部类。

5.2 内部类的形式

根据声明的位置:
    (1)成员-->类中方法外
        A:静态成员内部类--》简称静态内部类
        B:非静态成员内部类--》简称为成员内部类
    (2)局部-->方法体内,代码块(很少,语法上允许,但没见过
        A:名字局部内部类--》简称局部内部类
        B:匿名的局部内部类--》简称匿名内部类

5.3 内部类分类1-静态内部类

/ *
 * 一、静态内部类
 * 1、语法格式:
 * 外部类{
 * 		【修饰符】 static class 内部类 【extends 父类】【implements 接口】{
 * 		}
 * }
 * 
 * 2、修饰符
 * 权限修饰符:public,protected,缺省,private
 * 其他修饰符:
 * 		static:必须的
 * 		final:可以   不能被继承
 * 		abstract:可以   抽象类,要被子类继承
 * 
 * 回忆:// abstract static不能同时修饰方法。
 * 
 * 3、静态内部类的成员(都可以)
 *1)属性
 * 		静态属性:类变量
 * 		非静态属性:实例变量
 *2)构造器
 * 		无参
 **3)方法
 * 		静态方法
 * 		非静态方法
 * 		抽象方法:必须在抽象类中
 *4)代码块
 * 		静态代码块
 * 		非静态代码块
 *5)内部类(语法上可以,但不这么写)
 * 
 * 4、创建对象
 *1)在外部类中:
 * 		Inner in = new Inner();直接用
 *2)在外部类的外面
 * 		Outer.Inner in1 = new Outer.Inner();
 * 		Outer.Inner in2 = Outer.getInner();//开发中比较常见
 * 
 * 5、内部类的全名称
 *.外部类名.内部类名
 * 
 * 6、使用的方式
 *1)静态的内部类中使用外部类的成员?
 * 限制:不能用外部类的非静态的成员

 *2)在外部类中使用静态内部类的成员?
 * 静态内部类的静态成员:  静态内部类名.
 * 静态内部类的非静态成员: 静态内部类对象.

 * 问?用static修饰的类不能创建对象?(对/错)   错误

 *3)在外部类的外面使用静态内部类的成员?
 * 静态内部类的静态成员:  外部类名.静态内部类名.xx
 * 静态内部类的非静态成员: 静态内部类对象.xx
 *    静态内部类的对象在外面如何创建?   外部类名.静态内部类名  对象名 = new 外部类名.静态内部类名();

 * 3、字节码文件名:外部类名$静态内部类名.class
 */

5.5 内部类分类2-非静态内部类

/ *
 * 1、如何声明
 *{
 * 	【修饰符】  class  静态内部类名  【extends 父类】 【implements 接口们】{
 * 	}
 * }

 * 2、修饰符
 * 权限修饰符:public,protected,缺省,private
 * 其他修饰符:
 * 		final:可以   不能被继承
 * 		abstract:可以   抽象类,要被子类继承
 * 
 * 3、非静态内部类的成员:不能静态的成员
 *1)属性
 * 		非静态属性:实例变量
 *2)构造器
 * 		无参
 **3)方法
 * 		非静态方法
 * 		抽象方法:必须在抽象类中
 *4)代码块
 * 		非静态代码块
 *5)内部类(语法上可以,但不这么写)
 * 
 * 为什么?非静态内部类Inner对于外部类Outer来说它是一个非静态的成员,需要外部类的对象,才能访问。
	不允许静态成员,除了静态的常量
二、使用方式
 *1) 在外部类的外面,调用非静态内部类的成员?
 * 		用非静态内部类的对象.
 * 
 *2)在非静态内部类中,使用外部类的成员?
 * 		都可以用
 *     在外部类中使用非静态内部类的成员?
 *      非静态内部类的对象.
 *3)在外部类中:只能在外部类的非静态成员中使用
 * 		Inner in = new Inner();直接用
 *4)在外部类的外面
 * 		//1、方式一:
 *  	 Outer out = new Outer();
 *  	 Outer.Inner in = out.getInner();
 *  	//2、方式二:
 *   	 Outer.Inner in2 = new Outer().new Inner();//需要用外部类的对象,才能访问非静态内部类的构造器
 * 3、字节码文件名:外部类名$非静态内部类名.class

5.6 内部类分类3-局部内部类

和局部变量的属性类似:作用域和里边成员不能有静态成员除了静态的常量(最没用,最少用)
 * 1、语法格式:
 * 外部类{
 * 		方法{
 * 			【修饰符】  class 内部类 【extends 父类】【implements 接口】{
 * 			}
 * 		}
 * }
 * 
 * 
 * 2、修饰符
 * 	final:可以   不能被继承
 * 	abstract:可以   抽象类,要被子类继承
 * 
 * 3、局部内部类的成员:不能静态的成员(内部类里只静态内部类才能静态成员
 *1)属性
 * 		非静态属性:实例变量
 *2)构造器
 * 		无参
 **3)方法
 * 		非静态方法
 * 		抽象方法:必须在抽象类中
 *4)代码块
 * 		非静态代码块
 *5)内部类(语法上可以,但不这么写)
 * 
 * 
 *  4、创建对象
 *    (局部内部类作用域的限制)
 *    
 *  5、内部类的全名称
 * 	    包名.外部类名.数字编号内部类名
 *      因为它的字节码文件名:外部类$数字编号内部类.class  
 * 
 * 6、 在外部类的外面,调用局部内部类的成员?
 * 		不能用
 * 
 * 7、在局部内部类中,使用外部类的成员?
 *1)使用外部类的成员变量:受内部类的所在方法的影响
 * 		所在方法是静态的就不能用非静态的成员
 * 
 *2)使用内部类所在方法的局部变量
 * 	该局部变量必须是final声明的。
 * JDK1.8之前:手动加final
 * JDK1.8之后:自动加final
 * 
 * 原因:因为final修饰的变量是存储在方法区的常量池中,它的生命周期会更长
 * 问?在外部类的外面是否可以获取局部内部类的对象?可以

5.7 内部类分类4-匿名内部类

一、如何声明
new 父类(【实参列表】){
}
new 父接口(){
}

二、特点
1、可以拥的成员:
	不允许静态成员,除了静态的常量
2、其他特点
(1)在声明类的同时,要创建匿名内部类的对象,而且一个匿名内部类只有唯一一个的对象。
(2)匿名内部类是无法声明构造器
3、使用方式
(1)凡是局部内部类的限制对于匿名内部类都一样。
4、字节码文件名:外部类名$编号.class

三、常见的使用匿名内部类的形式
1、通过父类、父接口的变量名进行多态引用
Object obj = new Object(){
	//.....
};

2、匿名内部类的匿名对象.方法等
new Object(){
	public void test(){
	}
}.test();

3、匿名内部类的对象作为实参或表达式的一部分
class Test{
	public static void main(String[] args){
		test(new MyInter(){
			//重写抽象方法
		});
	}
	public static void test(MyInter my){
		//...
	}
}

5.8 抽象类

5.8.1 什么情况下会用到抽象类

(1)当父类需要表明他的子类们应该具备什么样的功能(方法),但是在父类中又无法给出具体的实现,例如:图形类Graphic,觉得他的子类们应该具备getArea求面积,getPremeter()求周长,但是在父类中又无法给出合理的具体实现,那么在父类中把这样的方法声明为抽象的方法,不写方法体。

(2)当父类仅仅用于表示一个抽象的概念,不希望使用者直接创建它的对象,而是在使用者去创建它更具体的子类的对象,那么这样的父类,虽然没有抽象方法,但是也会被设计为抽象类。

5.8.2 如何声明抽象类:(包含的静态方法可以直接通过”类名.方法名“调用)

抽象类的声明格式:
【修饰符】 abstract class 类名 【extends 父类】 【implements 接口们】{
}
抽象方法声明格式:(抽象方法不能有方法体)
【修饰符】 abstract 返回值类型 方法名(【形参列表】;

5.8.3 抽象类的特点

(1)不能实例化,不能直接创建对象,但是可以创建他的子类的对象
(2)如果一个类包含抽象方法,那么这个类必须是抽象类
当然一个抽象类,可以没有抽象方法,目的只有一个,就是不想让你创建他的对象
(3)抽象类就是用来被继承的,子类继承抽象父类,那么一定要实现(重写)父类的抽象方法,否则这个子类也得是抽象类。
(4)抽象类可以与子类的对象之间构成多态引用。

5.8.4 抽象类与非抽象类的不同?

(1)抽象类abstract修饰,可以有抽象方法,不可以直接创建对象
(2)非抽象类没abstract修饰,不能有抽象方法,可以直接创建对象

5.8.5 说明:抽象类可以包含类的所的成员

(1)属性:类变量、实例变量(可以)
(2)构造器:无参,参		(可以)用来为抽象类的属性初始化
(3)方法:静态方法,非静态方法,抽象方法,非抽象方法  (可以)
(4)代码块:静态代码块、非静态代码块   (可以)作用:初始化)
(5)内部类:可以

5.8.6 abstract关键字

是修饰符
可以修饰:(1)类内部类,外部类(2)方法

修饰方法时不可以和那些一起使用?
(1)final
(2)static
(3)private

修饰类时不可以和那些一起使用?
(1)final

外部类类的修饰符:public ,缺省,final
内部类? 

5.9 接口

1、什么情况下要声明接口?(理解,体会)接口和类相似但不同与类

(1)当多个类具相同的行为特征,但是这些类又没父子类的关系,那么可以通过抽取这些共同的行为特征到接口中,然后通过接口来统一管理他们。(个人理解是不同属性的类似方法体过程提取到接口)
(2)接口是一种行为标准,例如:比较大小、操作数据库、对象的序列化等,如果想要符合这个标准,就可以让自己的类实现这个接口即可。
(3)面向对象的编程原则:面向接口编程,目的是解耦合。

2、如何声明一个接口?

【修饰符】 interface  接口名  【extends 接口们】{
}

3、类如何实现接口

语法格式1:实现接口
【修饰符】 class  实现类名   implements 接口们{
}

语法格式2:实现接口&继承父类,继承父类一定要在前面
【修饰符】 class  子类名   extends 父类   implements 接口们{
}

3、接口的特点

(1)接口不能实例化,即不能直接创建对象
(2)接口可以与实现类的对象构成多态引用
(3)接口中的成员
JDK1.8之前:
A:public static final:常量
B:public abstract:抽象方法
C:内部接口
JDK1.8之后新增:
D:public static :公共的静态方法:【修饰符】 static 返回值类型 方法名(【形参列表】{方法体} //静态方法的调用:接口名.静态方法
E:public default:公共的默认方法:【修饰符】 default 返回值类型 方法名(【形参列表】{方法体} //默认方法的调用,必须用实现类的对象.方法名调用
(4)接口就是用来被实现的。一个类实现接口时,必须要重写(实现)接口的所有的抽象方法,否则这个类也得是抽象类。
(5)一个类可以同时实现多个接口。
(6)一个类可以同时继承父类,又实现接口,那么这个时候必须先继承,后实现接口。
(7)接口也可以继承多个接口:接口支持多层继承也支持多个父亲!
(8)接口中不谈:构造器、代码块、普通的属性、普通的方法。

4、JDK1.8的静态方法和默认方法

静态方法:接口名.静态方法名调用
默认方法:接口的实现类的对象.方法名

5、默认方法的冲突问题

(1)一个类实现了多个接口,多个接口中有相同的默认方法
实现类可以择保留其中一个或多个,通过“接口名.super.方法”保留;
实现类也可以选择一个都不保留,完全重写。
(2)一个类继承了父类,又实现了接口,当父类中有与接口的默认方法一样的方法时
默认:保留的是父类的
主动:如果要保留父类的,通过“super.方法”
如果要保留父接口的,通过“接口名.super.方法”保留;
可以选择都不要,完全重写

6、属性冲突问题

一个类继承了父类,又实现了接口,父类中的常量属性与接口中的常量属性,名称和类型一致时,会导致错误,xx 属性 是模糊不清的(is ambiguous)
“super.属性”代表父类的
“接口名。属性”代表是接口的

7、静态方法冲突

一个类继承了父类,又实现了接口,父类中有和接口中一样的静态方法时,默认选择的是父类。

5.10 接口与抽象类的区别

接口与抽象类的区别?

(1)修饰符
抽象类是用abstract class声明的,接口使用interface 声明的
(2)成员不同
抽象类可以有非常量的属性、构造器、非抽象的普通方法、代码块等
接口中只能常量,抽象方法,内部接口,JDK1.8之后增加静态方法和默认方法。
(3)使用不同
抽象类:用来被继承的,单继承的限制
接口:用来被实现的,一个类可以同时实现多个接口,接口还可以继承多个接口。

接口与抽象类的相同点?

(1)不能实例化
(2)它俩都可以有抽象方法
(3)使用时都是与子类或实现类的对象之间构成多态引用

5.11 类加载初始化过程

一、类的初始化:clinit<>()的代码由两部分组成:
    A:静态变量的显示初始化代码
    B:静态代码块代码
    	同样,A和B谁在上面谁先执行(强调无静态构造器)

二、实例初始化:init<>()
    A:非静态变量的显示初始化
    B:非静态代码块
    C:构造器中的代码init
	顺序:C永远在最后执行,A和B谁在上谁先执行

三、特殊情况:静态代码块里有new T()
    A:静态变量赋值内容是new T(),即静态代码有new内容,
    	这个赋值便是调用了init方法,便是在clinit初始化过程中插进了init方法
    	后续不变,main()中有new仍有调用init方法
    B:父clinit -> 子clinit -> 父init -> 子init

5.11.1 案例1

package com.atguigu.exer1;

public class TestStaticExer1 {
	public static void main(String[] args) {
		Son son = new Son();
	}
}
class Father{
	static{
		System.out.println("(1)父类的静态代码块");
	}
	{
		System.out.println("(2)父类的构造器");
	}
	Father(){
		System.out.println("(3)父类的无参构造");
	}
}
class Son extends Father{
	static{
		System.out.println("(4)子类的静态代码块");
	}
	{
		System.out.println("(5)子类的构造器");
	}
	Son(){
		System.out.println("(6)子类的无参构造");
	}
}
// 结果:(1)(4)(2)(3)(5)(6)

5.11.2 案例2

package com.atguigu.exer2;

public class TestStaticExer2 {
	public static void main(String[] args) {
		Zi zi = new Zi();
	}
}
class Fu{
	private static int i = getNum("(1)i");
	private int j = getNum("(2)j");
	static{
		print("(3)父类静态代码块");
	}
	{
		print("(4)父类构造代码块");
	}
	Fu(){
		print("(5)父类构造器");
	}
	public static void print(String str){
		System.out.println(str + "->" + i);
	}
	public static int getNum(String str){
		print(str);
		return ++i;
	}
}
class Zi extends Fu{
	private static int k = getNum("(6)k");
	private int h = getNum("(7)h");
	static{
		print("(8)子类静态代码块");
	}
	{
		print("(9)子类构造代码块");
	}
	Zi(){
		print("(10)子类构造器");
	}
	public static void print(String str){
		System.out.println(str + "->" + k);
	}
	public static int getNum(String str){
		print(str);
		return ++k;
	}
}
答案:
(1)i->03)父类静态代码块->16)k->08)子类静态代码块->12)j->14)父类构造代码块->25)父类构造器->27)h->19)子类构造代码块->210)子类构造器->2

5.11.3 案例3

package com.atguigu.exer3;

public class TestStaticExer3 {
	public static void main(String[] args) {
		MyClass obj = new MyClass();
	}
}
class MyClass{
	static{
		i = 100;//可以
//		i++;//错误
		MyClass.i++;
//		System.out.println("(1)静态代码块 i=" + i);//错误
		System.out.println("(1)静态代码块 i=" + MyClass.i);
	}
	{
		j = 100;
//		j++;
		this.j++;
//		System.out.println("(2)构造代码块j=" + j);
		System.out.println("(2)构造代码块j=" + this.j);
	}
	MyClass(){
		j = 200;
		j++;
		System.out.println("(3)构造器j=" + j);
	}
	private static int i = getNum("(4)i");
	private int j = getNum("(5)j");
	
	public static void print(String str){
		System.out.println(str + "->" + i);
	}
	public static int getNum(String str){
		print(str);
		return ++i;
	}
}
答案:
(1)静态代码块 i=1014)i->1012)构造代码块j=1015)j->1023)构造器j=201

5.11.4 案例4

package com.atguigu.exer4;

public class T {
	public static int k = 0;
	public static T t1 = new T("t1");
	public static T t2 = new T("t2");
	public static int i = print("i");
	public static int n = 99;
	
	public int j = print("j");
	{
		print("构造块");
	}

	static{
		print("静态块");
	}
	public T(String str){
		System.out.println((++k) + ":" + str + "  i=" + i + "  n=" + n);
		++n;
		++i;
	}
	public static int print(String str){
		System.out.println((++k) + ":" + str + "  i=" + i + "  n=" + n);
		++n;
		return ++i;
	}
	public static void main(String[] args) {
	
	}
}
答案:
1:j  i=0  n=0
2:构造块  i=1  n=1
3:t1  i=2  n=2
4:j  i=3  n=3
5:构造块  i=4  n=4
6:t2  i=5  n=5
7:i  i=6  n=6
8:静态块  i=7  n=99

你可能感兴趣的:(java,java,jvm,开发语言)