类的5个成员:属性、方法、构造器、代码块、内部类
Java的3大基本特性:封装、继承、多态
----封装:安全、简单方便、对使用者隐藏实现细节
----继承:代码复用、扩展,表示is-a的关系
----多态:提高代码灵活性,增强代码功能
属性:类的数据描述特征
在类中方法外
【修饰符】 数据类型 属性名;
(1)当创建对象后,没为属性赋值,那么
属性默认值和数组的元素的默认值是一样的
:
byte,short,int,long:0
float,double:0.0
char:\u0000或说是Unicode编码为0的字符
boolean:false
引用数据类型:null
(2)每一个对象的属性在堆中是互相独立
(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)本类中
★直接属性名
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)
声明的位置不同
局部变量:(1)方法体{}(2)形参(3)代码块中
成员变量:声明的位置,类中方法外:(1)类变量:static修饰(2)实例变量:没static修改
(2)存储的位置不同
局部变量:栈
成员变量:堆
(3)初始值的获取方式不同
局部变量:必须手动初始化
成员变量:如果没初始化,它默认值,也可以手动初始化(方式很多种)
(4)生命周期
局部变量:短,从代码运行到声明它的语句开始,到它的作用域结束就结束,每一个线程,每一次调用执行都是全新的生命周期
实例变量:随着对象的创建而出生,随着对象的回收而消亡,和对象同生共死
类变量:最长?
(5)作用域
局部变量:从声明处开始,到它所属的}结束
成员变量(实例变量、类变量)不谈作用域,在本类中,直接访问,其他类中用"对象."访问
(6)修饰符
局部变量:要么没,要么只能一个final
成员变量:可以没,如果可以很多…
一个类的属性,对外界想要控制访问,那么可以给属性设置修饰符,例如:private
权限修饰符/访问控制修饰符:可见的范围从小到大
范围 | 本类 | 本包 | 其他包子类 | 其他包非子类 |
---|---|---|---|---|
private | 可以 | 不可以 | 不可以 | 不可以 |
缺省 | 可以 | 可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
public | 可以 | 可以 | 可以 | 可以 |
权限修饰符可以修饰:属性、方法、构造器、内部类
其中的public和缺省,还可以修改类
回忆:
属性的声明: 【修饰符】 数据类型 属性名;类的声明: 【修饰符】 class 类名{ }
Java的内存分析图:
Java程序是运行在JVM中,JVM的功能之一:内存管理
(1)虚拟机栈,简称栈:存储局部变量
(2)堆:存储new出来的实例对象,包括数组
(3)方法区:用来存储不怎么变的东西,例如:加载的类的信息、静态的变量等
(4)本地方法栈:native等C语言实现的方法的变量等
(5)程序计数器:每一个线程当前运行到哪一个指令等信息
JVM高级时,还会再详细讲解,内存管理,以及垃圾回收的算法,内存优化
2、对象的内存结构图
对象的属性:存储在堆中
每一个对象的属性:每一个对象是独立的
对象数组:它的元素数据类型是引用数据类型
例如:String[] ,Student[], Circle[]等
元素的数据类型[] 数组名;
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;
}
方法(Method,又称为函数(Function,表示一个独立的功能。
在Java中作为类的成员。
1、先声明后使用(和变量、类等一样)
2、不调用不执行,调用一次执行一次
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)如果被调用的方法的形参列表不是空的(形参列表)
那么调用时,一定要传实参的,而且实参的类型(一致或兼容)、个数、顺序与形参列表要一一对应。
(1)本类中直接用,其他类用对象.
(2)返回值就接收,无返回值就别接收
(3)形参就传实参,没形参就不传实参,实参的作用就是给形参传值
(4)返回值类型:如果是void,说明没有返回值
如果是其他的,说明有返回值
方法形式有四种:
1、无参无返回值
2、有参无返回值
3、无参有返回值
4、有参有返回值
方法的参数的传递:实参-->形参(值传递)
形参:声明/定义方法()中的参数,被调用方法的()中的参数
实参:调用方法()中的参数
`实参给形参传 数据值`
`形参对值的修改 不会 影响实参的值`
`实参给形参传 地址值`
`形参对象对属性的修改 会 影响实参对象的属性`(本质上它俩就是同一个对象)
(陷阱)当形参重新new对象了,那么和实参就没有关系了,形参在修改属性,和实参就没关系了
(特殊)String类型比较特殊,它的对象有不可变性,所有的改变都会产生新的String对象,类似这样的对象,还有包装类对象等
在同一个类中,出现了方法名称相同,但是形参列表不同的两个或多个方法,称为方法的重载。
和返回值类型无关。
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;
}
给main()传递的实参,叫做命令行参数。
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);
}
}
格式:(数据类型... 可变参数名)
(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;
}
(1)属性的封装
:
一般情况下:
权限修饰符:private私有化
提供标准的get/set方法
(2)方法的封装
:封装的是一个独立的功能,一段代码
(3)类的封装
(4)组件的封装
:例如使用第三方的组件,支付宝,网银支付,对方给你开放了接口,你按照它规定的格式,把数据传给它,它计算完,把结果返回。
(5)系统的封装
:对于使用者来说,不知道实现细节,但是知道怎么用。
(1)get方法:
【修饰符】 属性的类型 get属性名首字母大写(){
return 属性;
}
(2)set方法:
【修饰符】void set属性名首字母大写(属性的类型 形参名){
....
this.属性 = 形参名;
}
(3)特别的是Boolean类型的属性,is属性名(属性的类型 形参名)
(4)最好保留无参构造
(1)和new一起使用时,用来创建对象:
new新对象时意思就是在运行这个构造器
new 构造器名(参数列表)
(2)构造器可以为属性赋初始值
1、构造器的声明:位置:类中,方法外
语法格式:
//无参构造
【修饰符】 构造器名(){
}
//有参构造
【修饰符】 构造器名(形参列表){
}
说明:构造器没有返回值类型,也不写void
2、如何调用构造器:
(1)在非本类构造器中
调用无参构造:
new 构造器名()
调用有参构造:
new 构造器名(实参列表)
(2)在本类的构造器中?(留着)
(3)在子类的构造器中?(留着)
3、所以,创建对象的格式:
之前:
类名 对象名 = new 类名();
现在:
类名 对象名 = new 构造器名();//无参构造
类名 对象名 = new 构造器名(实参列表);//有参构造
(1)构造器的名称必须与所在的类名完全相同
(2)所有的类都有构造器,如果一个类没有显式声明构造器,
那么这个类在编译时,将自动生成一个无参的构造器。
如果显式声明了构造器,那么编译器将不再添加默认的无参构造。
(3)构造器可以重载
(4)构造器没返回值类型
(5)构造函数的修饰符只能是权限修饰符(public、缺省、protect、private)
其他的(static、final、abstract)都不行
this的意思:当前对象,本类的调用
(1)构造器中
this当前对象表示的是正在new(创建)的那个对象
(2)成员方法中(非静态方法中)
this当前对象表示的是调用该方法的对象
(3)静态方法中不能用this关键字
用法:
(1)
this.属性 或 this.成员变量
当在某个方法中(构造器、成员方法),如果出现了局部变量(例如:形参和成员变量)属性重名了,
那么用“this.属性”进行区分。
如果该方法中不涉及到局部变量与属性重名问题,在属性的前面加不加this.都可以。
(2)this.方法
用this.方法表示访问当前对象的方法,完全可以省略this.
(3)this()
或this(实参列表)
用于调用本类的其他构造器,必须在构造器的首行。第一句。要避免递归调用。
this()用于表示调用本类的无参构造
this(实参列表)用于表示调用本类的有参构造
(1)避免类的重名
有了包之后,类的全名称就变成了:包.类名
(2)管理类等资源文件
(3)顺便,控制某些类、成员的访问权限
格式:package 包名;
位置:必须在源文件的首行
包名,全部单词都小写,单词之间使用.分割,一般使用域名倒置 + 模块名
(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:政府
前提:被使用的类要具有可见性
如果被使用的类的修饰符是缺省的,那么不能跨包使用
如果被使用的类的修饰符是public的,那么可以跨包使用
同样:被使用的类的成员,也要注意“权限修饰符”的问题
如何使用:
(1)使用全名称
com.meituan.other.Teacher tea = new com.meituan.other.Teacher();
(2)导包语句 + 简名称
导包语句:
import com.meituan.other.Teacher;
代码中:
Teacher tea = new Teacher();
1、必须在package的后面,所有class声明的上面。
2、import在package和class之间,可以并列很多句。
形式:
(1)import 包.类名;
(2)import 包.*; 省略最后一级的类名,不能省略子包名
(3)静态导入:JDK1.5之后引入
import static 包.类.静态成员名;
import static 包.类.*;
当用到两个包的类,并且这两个类的名字一样:
其中一个用全名称,只有一个使用import或两个都用全名称
特别说明
同时使用名称相同,但是包名不同的两个类
例如:
java.util.Date
java.sql.Date
(1)编译
javac -d . 源文件名.java
-d:创建包目录结构
.:在当前目录下创建包目录结构
(2)运行
java 包.类名
从代码的角度:
(1)自上而下:当设计一个新的类时,发现已经有一个类的所有属性,和方法,已经写好,而且正好也是我要的,可以继承它,再扩展它没有的部分
(2)自下而上:当发现设计了多个类时,这些类又一些共同的特征,那么可以把这些共同的特征(属性、方法)可以抽取到父类中,这样就不用多个类中重复编写。
目的:代码的复用、扩展
从逻辑角度的目的:用继承表示is-a的关系
语法格式:
【修饰符】 class 子类名 extends 父类名{
}
子类(subclass):又称为派生类
父类(superclass):又称为超类、基类
super的意思:父类的调用
用法:
前提:对应的父类的属性或方法、构造器没私有化
需要用:当子类声明了和父类非私有的同名的属性时,如果想要在子类中访问父类的这个属性时,那么需要加super.
设计者的角度应该避免
可用可不用的:当子类中没与父类非私有属性同名时,那么想要在子类中访问父类的属性时,那么可以加super.也可以不加
message:就近原则,访问方法里的局部变量。
this.message:局部变量与成员变量重名时,访问子类的成员变量属性。
super.message:不管重不重名都访问父类的变量属性。
需要用:当子类重写了父类的某个方法时,在子类中又要调用父类被重写的方法时,那么需要加super.
可用可不用:当子类没重写父类的方法时,在子类中想要调用父类的方法,可以加super.也可以不加
super()或super(实参列表)必须在子类构造器的首行
默认:super() 父类必须无参构造
手动:super(实参列表) 父类必须有有参构造,
当父类没无参构造时,必须在子类的构造器的首行用这句话引用父类的有参构造。
当父类有无参构造,如果你想要调用父类的有参构造,也可以使用这句话在子类构造器的首行调用。
补充说明:
this:
* this.属性,this.方法,他们的追溯不仅限于本类中,只要当前对象可访问都可以,
可以是本类声明的,也可以是从父类继承的。
* this()和this(实参列表)必须是本类。
super:
* super()或super(实参列表)必须是直接父类的。
* super.属性,super.方法,他们的追溯也不仅限于直接父类,可以间接父类的。
* 如果直接父类中找到了,就不往上了,如果没找到,会一直往上,直到找到为止。
当父类的某个方法的方法体(方法的实现)不适合与子类时,那么子类应该选择重写该方法。
1、重写的要求:
(1)两同:方法名与形参列表必须相同
(2)两小
返回值类型
:
------基本数据类型和void:子类重写的方法的返回值类型 与 父类被重写的方法的返回值类型 相同
------引用数据类型:子类重写的方法的返回值类型 <= 父类被重写的方法的返回值类型
------抛出的异常类型:同样是 <=
(3)一大:
权限修饰符
:子类重写的方法的权限修饰符 >= 父类被重写的方法的权限修饰符
------ public > protected > 缺省(没写权限修饰符> private
2、哪些方法不能被子类重写?
(1)父类的private方法是否可以被重写?
------------不可以,因为父类的私有的方法,在子类中不可见
(2)父类的final方法不能被重写
(3)父类的static方法不能被重写
3、Overload、Override的区别?
Overload:方法的重载
Override:方法的重写
方法的重载 | 方法的重写 | |
---|---|---|
声明的位置 | 同一个类 | 父子类中 |
方法名 | 必须相同 | 必须相同 |
形参列表 | 必须不同 | 必须相同 |
返回值类型 | 无关 | <=基本数据类型和void,必须相同;引用数据类型,可以“子类<=父类” |
抛出的异常 | 无关 | <= |
权限修饰符 | 无关(建议一致) | >= public > protect > 缺省 > private //“子类>=父类” |
(1)方法的重载与重写
(2)对象的多态性
,体现在编译时类型与运行时类型不一致,编译时看“父类”,运行时“看子类”,执行的是子类重写的方法体。
个人理解:
1、子类的new对象:可以赋给父类的变量
2、执行时候变量.方法调用子类方法
3、属性还是按照变量的方法使用
(1)继承
(2)重写
:父类和子类有同名的方法。
(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方法
--案例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
(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();
}
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){//编译不通过}
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);
}
}
// 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){
// //省略代码
// }
}
// 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){
}
}
声明的位置:类中方法外
声明的格式:
类{
【static】{
}
}
分类:
静态代码块和非静态代码块
1、声明的位置:类中方法外
2、声明形式:
(1)非静态代码块
{
语句;
}
(2)静态代码块
static{
语句;
}
A:每次创建对象时自动执行。
本质上:Java编译器,会把我们的
(1)实例变量的显式初始化
(2)非静态代码块
(3)构造器的代码
B:合起来编译为一个(),实例初始化方法。
特别说明:
(1)显式初始化和
(2)非静态代码块谁在上面谁先执行,构造器永远是最后执行
并且()方法的执行是一起执行的,不会拆开。
C:子类的方法执行会先导致父类的方法执行。
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;
}
}
1、java.lang.Object类是超级父类。它是所类的根父类。
例如:数组类型:int[],String[],Customer[],int[][],String[][],包括数组
(1)它的所属性、方法都可以被所有类型继承。
换句话说,所有子类(所有类)型都拥Object类中的方法
(2)它的无参构造一定会被调用
(3)Object类型变量、形参、元素可以接收任意类型的对象
boolean equals(Object obj)
和 int hashCode()
(1)public boolean equals(Object obj):做比较
区别:==和equals
如果子类没重写equals()方法,那么Object默认的equals方法的实现,和“==”是一样的。
如果子类希望equals()比较的不是对象的地址,而是其他信息,
例如:属性的值等,那么就要重写equals方法。
例如:String等都是重写了equals方法
默认是两个对象的地址
子类可以重写,重写后一般比较属性的内容
(2)public int hashCode():返回哈希值,例子用int接受的
hashCode()返回当前对象的哈希值
这个值只是在当把对象放到类似于“哈希表”等容器中时才用,其他时候没用。
后面会学习把对象放到“Hashtable,HashMap,HashSet等集合容器”中时,才会用到。
结论:
public boolean equals(Object obj)
public int hashCode()
形影不离,总是同时被重写。而且参与equals比较的属性,一定要参与hashCode值的计算。
两个方法想重写的话,快捷键就是Alt+shift+s选择重写他俩选择哪些些属性就了
(1)如果两个对象的hashCode()值不相同,我们就断定这两个对象一定不相同:
就可省略equals,肯定是false
(2)如果两个对象equals比较相等,那么这两个对象的hashCode值一定相同
(3)如果两个对象的hashCode()值相同:
那么我们调用equals比较的结果可能为true,也可能为false
Class getClass()
和 void finalize()
和 String toString()
(3)public 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());
(4)protected void finalize():这个方法是由GC(垃圾回收程序调用,不是由程序员调用
一、什么是垃圾?
MyData my = new MyData();
//for中的局部变量,每循环一次,上一次的my就失去生命了,右边的对象就是垃圾了
Java的垃圾回收器的特点:它什么时候来回收,是不确定的。一般:
(1)内存吃紧了
(2)通知它来回收
(3)System.gc();//这个通知垃圾回收器过来回收,实际时间是系统自己的安排
二、什么时候调用?什么样的代码会写在这个方法中?
一般是资源对象会重写这个方法,然后会在这个方法中写彻底释放资源的代码。
资源对象:IO流,数据库连接
三、这个方法一个特点?
如果在finalize()方法中,使得这个对象“复活”了,
(即在方法中,使得另一个有效变量指向当前对象this,那么当前对象this就“复活”)
一旦“复活”垃圾回收器就不能回收他了,等他下次再次变成垃圾后,垃圾回收器,
就不再第二次调用这个对象的finalize方法,直接回收。
即每一个对象的finalize()只能被调用一次。
(5)public String toString():
1、默认返回的是:类名@哈希值的十六进制形式
2、子类可以重写
3、如果直接打印一个对象,或者是一个对象与字符串进行“+”拼接时:
默认自动调用该对象的toString()
(意思就是:
1)把正常的getInfo方法换成快捷键生成toString方法
2)然后调用打印时候直接打印对象就行,不用写“arr[i].getInfo”了
3)直接写arr[i]就行,因为自动调用toString方法
)
static:静态的(不能修饰外部类)
可以修饰:
内部类、
属性(称为静态变量、类变量)、
方法(称为静态方法、类方法)、
代码块(称为静态代码块)
用static修饰的属性,称为静态变量,类变量。
特点:
(1)不管创建多少个该类的实例对象,静态变量只一份。即所有对象共享。存储在方法区。
(2)类变量是在类初始化时初始化。因此,比创建对象早,比实例变量初始化早。
静态变量的get/set也是静态的,建议用"类名."调用,当然也可以用"对象."
用static修饰的方法,称为类方法,静态方法;方法里只能访问static类型的变量属性,不能有非static类型的成员!
特点:
(1)可以用"类名."调用,不需要对象(不用创建对象,类名.方法就可以直接调用)。
例如:Arrays,Math
(2)static方法中,不能直接使用本类的非静态的属性、方法、this关键字、super关键字
用static修饰的代码块,称为静态代码块。
作用:为静态变量初始化
执行特点:
(1)在类初始化时执行,而且只执行一次。比创建对象要早,比调用方法要早。
(2)本质上,类初始化其实是一个方法(),叫类初始化方法。类的初始化只有一边,无论怎么调用都只初始化一遍
-------这个()方法的代码由:
--------------A:静态变量的显式初始化代码
--------------B:静态代码块代码
-------同样,A和B谁在上面谁先执行。(强调无静态构造器)
(3)子类的初始化会先检查父类是否初始化,如果父类没初始化,那么会先初始化父类。
…
变量:
本质上是代表内存的一块存储区域。
三个要素:
(1)数据类型
(2)变量名
(3)变量值
变量的使用要注意:
(1)先声明后使用
(2)使用之前要初始化值
(3)要注意注意域
变量的分类:成员变量 & 局部变量
(1)静态的成员变量:类变量,静态变量
(2)非静态的成员变量:实例变量,非静态变量
声明的位置:类中方法外
存储的位置:
(1)静态变量:方法区
(2)实例变量:堆
生命周期:
(1)静态变量:和类同生死共存亡
(2)实例变量:和对象的声明周期一样。
作用域:
(1)静态变量:本类中,直接用,其他类,可以通过对象.也可以通过类名.访问
(2)实例变量:本类中,直接用,其他类,通过对象.
赋值:
(1)静态变量:默认值、显式初始化或静态代码块、set方法
(2)实例变量:默认值、显式初始化或非静态代码块、构造器、set方法
修饰符:
(1)静态变量:权限修饰符、static、final
(2)实例变量:权限修饰符、final
声明的位置:
(1)方法体{}中:最常见
(2)方法头()中:形参 方法头也称为方法签名。
(3)代码块中:很少见。
存储的位置:栈
生命周期:短 从执行到声明它的语句开始,到作用域就结束了。
一般是说和方法调用的生命周期差不多。
作用域:小 从声明处开始,到所属的}结束
赋值:
(1方法体{}:手动赋值 变量 = xx;
(2形参:调用方法时,由实参赋值
(3代码块:手动赋值 变量 = xx;
修饰符:final
修饰符:final,最终的
可以修饰什么?属性、方法、内部类、外部类、局部变量
1、类:包括内部类和外部类
2、变量:成员变量(类变量、实例变量和局部变量
3、方法
一、修饰类
表示这个类不能由子类,即不能被继承。 ————太监类
例如:String,System,Math
二、修饰方法
表示这个方法不能被重写。
可以被继承不能被重写。
三、修饰变量
表示这个变量的值不能被修改,即常量。导致这个变量无set方法
修饰变量,那么必须为这个变量手动初始化,不管是局部变量还是成员变量。
若此方法未初始化那么一定要有参构造才能初始化,不能有无参构造
修饰符:native:本地的
可以修饰?方法
修饰的方法表示该方法的方法体不是由Java语言实现的,
由非Java语言,例如:C、c++等实现的,编译成.dll文件,由Java调用。
对于Java程序来说,调用native修饰的方法,和调用其他的Java方法一样规则。
并且对于子类来说,也可以重写它。
Java的成员:
(1)属性( * * * * * )
(2)方法( * * * * * )
(3)构造器( * * * * * )
(4)代码块( * * )–》(1)面试(2)作用:为属性初始化,静态代码块为静态变量–》类初始化,非静态代码块为非静态变量–>实例初始化。
(5)内部类( * * )
1、概念:声明在其他类里面的类叫做内部类。我们把外面的这个类称为外部类。
2、回忆:
类的概念:一类具有相同特性的事物的抽象描述。
在描述一类事物时,发现它的内部也有一个独立的结构,也需要用一个类来描述。
而且这个内部的事物只为外部类服务,或者是要依赖于外部类,所以我们把这样的内部结构声明为内部类。
例如:身体Body,内部可能心脏Heart等
内部类它是可以使用外部类的所的成员和数据,包括私的。所以我们把这个类声明为内部类更方便。
现在内部类的使用的频率在JavaEE里面比较少,之前在JavaSE的GUI,Andriod等里面大量的使用内部类。
但是在我们接下来要学习的集合框架类中,会很多的内部类。
根据声明的位置:
(1)成员-->类中方法外
A:静态成员内部类--》简称静态内部类
B:非静态成员内部类--》简称为成员内部类
(2)局部-->方法体内,代码块(很少,语法上允许,但没见过
A:名字局部内部类--》简称局部内部类
B:匿名的局部内部类--》简称匿名内部类
/ *
* 一、静态内部类
* 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
*/
/ *
* 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
和局部变量的属性类似:作用域和里边成员不能有静态成员除了静态的常量(最没用,最少用)
* 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修饰的变量是存储在方法区的常量池中,它的生命周期会更长
* 问?在外部类的外面是否可以获取局部内部类的对象?可以
一、如何声明
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){
//...
}
}
(1)当父类需要表明他的子类们应该具备什么样的功能(方法),但是在父类中又无法给出具体的实现,例如:图形类Graphic,觉得他的子类们应该具备getArea求面积,getPremeter()求周长,但是在父类中又无法给出合理的具体实现,那么在父类中把这样的方法声明为抽象的方法,不写方法体。
(2)当父类仅仅用于表示一个抽象的概念,不希望使用者直接创建它的对象,而是在使用者去创建它更具体的子类的对象,那么这样的父类,虽然没有抽象方法,但是也会被设计为抽象类。
抽象类的声明格式:
【修饰符】 abstract class 类名 【extends 父类】 【implements 接口们】{
}
抽象方法声明格式:(抽象方法不能有方法体)
【修饰符】 abstract 返回值类型 方法名(【形参列表】;
(1)不能实例化,不能直接创建对象,但是可以创建他的子类的对象
(2)如果一个类包含抽象方法,那么这个类必须是抽象类
当然一个抽象类,可以没有抽象方法,目的只有一个,就是不想让你创建他的对象
(3)抽象类就是用来被继承的,子类继承抽象父类,那么一定要实现(重写)父类的抽象方法,否则这个子类也得是抽象类。
(4)抽象类可以与子类的对象之间构成多态引用。
(1)抽象类abstract修饰,可以有抽象方法,不可以直接创建对象
(2)非抽象类没abstract修饰,不能有抽象方法,可以直接创建对象
(1)属性:类变量、实例变量(可以)
(2)构造器:无参,参 (可以)用来为抽象类的属性初始化
(3)方法:静态方法,非静态方法,抽象方法,非抽象方法 (可以)
(4)代码块:静态代码块、非静态代码块 (可以)作用:初始化)
(5)内部类:可以
是修饰符
可以修饰:(1)类内部类,外部类(2)方法
修饰方法时不可以和那些一起使用?
(1)final
(2)static
(3)private
修饰类时不可以和那些一起使用?
(1)final
外部类类的修饰符:public ,缺省,final
内部类?
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、静态方法冲突
一个类继承了父类,又实现了接口,父类中有和接口中一样的静态方法时,默认选择的是父类。
接口与抽象类的区别?
(1)修饰符
抽象类是用abstract class声明的,接口使用interface 声明的
(2)成员不同
抽象类可以有非常量的属性、构造器、非抽象的普通方法、代码块等
接口中只能常量,抽象方法,内部接口,JDK1.8之后增加静态方法和默认方法。
(3)使用不同
抽象类:用来被继承的,单继承的限制
接口:用来被实现的,一个类可以同时实现多个接口,接口还可以继承多个接口。
接口与抽象类的相同点?
(1)不能实例化
(2)它俩都可以有抽象方法
(3)使用时都是与子类或实现类的对象之间构成多态引用
一、类的初始化: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
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)
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->0
(3)父类静态代码块->1
(6)k->0
(8)子类静态代码块->1
(2)j->1
(4)父类构造代码块->2
(5)父类构造器->2
(7)h->1
(9)子类构造代码块->2
(10)子类构造器->2
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=101
(4)i->101
(2)构造代码块j=101
(5)j->102
(3)构造器j=201
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