JAVA面向对象(一) 类的成员

一、类和对象的概念:

创建Java自定义类
步骤:

  1. 定义类(考虑修饰符 、类名)
  2. 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
  3. 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)

类的修饰符:public default
注:内部类除外,内部类除了可以作为类,还可以作为类的成员。

为什么static方法访问非static属性,编译不通过?
这是由类和对象的加载顺序决定的。
static类型的属性(类变量)、方法在类加载的link阶段的prepare子阶段就分配内存并初始化为0值了,然后在类加载的初始化阶段 初始化为自定义的值(这个初始化过程在类加载的连接阶段后先不执行,需要触发条件,比如new指令)。
而非static类型的属性(成员变量)是在对象创建时 先检查类是否加载,再分配内存后创建的,然后执行初始化为0值,设置对象头,再执行init初始化。

对象的创建和使用:
类名 对象名 = new 类名(); “对象名.对象成员”;
创建匿名对象:不定义对象的直接引用 (即对象名),new 类名(),
可以通过匿名对象直接调用这个对象的属性和方法,也可以在匿名对象中添加代码块(这也是map.put的常用方法),创建对象时会自动执行。
注:如果只需要对某个对象进行一次方法调用,那么就可以使用匿名对象。

return new HashMap<String,Object>(){
	{
		put("number",1);
		put("name","admin");
	}
};

二、类的成员

2.1 属性

1、属性的修饰符
常用的权限修饰符有:private、缺省、protected、public
其他修饰符:static、final

2、变量的分类:成员变量与局部变量
在方法体外,类体内声明的变量称为成员变量。
在方法体内部声明的变量称为局部变量。

成员变量:
实例变量(不以static修饰)
类变量(以static修饰)
局部变量:
形参(方法、构造器中定义的变量)
方法局部变量(在方法内定义)
代码块局部变量(在代码块内定义?)

3、成员变量(属性)和局部变量的区别?(下面各项,前者为成员变量,后者为局部变量)
声明的位置: 直接声明在类中; 方法形参或方法内部、代码块内、构造器内等。
修饰符: private、public、static、final等; 不能用权限修饰符修饰,可以用final修饰。
初始化值: 有默认初始化值 (0 null false); 没有默认初始化值,必须显式赋值,方可使用
内存加载位置: 堆空间 或 方法区的静态域内; 栈(虚拟机栈、本地方法栈)的栈帧中的局部变量表。

4、对象属性的默认初始化赋值:
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
char 0 或写为:’\u0000’(表现为空)
boolean false
引用类型 null

2.2 方法

声明:修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….)
Java里的方法不能独立存在,所有的方法必须定义在类里。

修饰符:public,缺省,private, protected,static等

没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。
方法中只能调用方法或属性,不可以在方法内部定义方法。

方法重载 overload
方法名相同,参数列表不同。与返回值类型无关,可以不同。
比如 print()函数,参数可以是double 也可以是int等等

可变形参:
允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
方法名(参数的类型名 …参数名);参数个数是可变多个:0个,1个或多个,0个就意味着可以空参。
在方法内,对可变参数的使用与数组是一致的;max(int …number) == max(int[] number),也即当成数组来用。
方法的参数部分有可变形参,需要放在形参声明的最后。
当可变形参的方法和确定形参的方法构成 重载时,优先调用确定形参的方法。

方法参数的值传递机制:
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参。被调用的方法不会影响 当前方法中 实参的值。
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。被调用的方法可能会影响 当前方法中 实参的值(引用 指向的 内存 可能会被修改)。

递归方法:一个方法体内调用它自身。
方法递归包含了一种隐式的循环可以代替循环),它会重复执行某段代码,但这种重复执行无须循环控制。但是一定要向已知方向递归(一定要设计好终止条件),否则这种递归就变成了无穷递归,类似于死循环。

2.3 构造器

构造器的特征:
它具有与类相同的名称;
它不声明返回值类型(与声明为void不同);
不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值。

构造器的作用:创建对象时给对象进行初始化

根据参数不同,构造器可以分为如下两类:
隐式无参构造器(系统自带,无需定义)
显式定义一个或多个构造器(无参、有参),构造器内主要是初始化语句。

注:
Java语言中,每个类都至少有一个构造器;默认构造器的修饰符与所属类的修饰符一致;
一个类可以创建多个重载的构造器;一旦显式定义了构造器,则系统不再提供默认构造器
父类的构造器不可被子类继承,可以通过super调用。

构造器在字节码文件中,将会被编译成 方法执行。
与 ?
是类初始化方法,只有一个,因为类只会被加载一次,在类加载的初始化阶段被调用,用于初始化类属性(静态属性)。
可以声明多个,创建对象的初始化阶段被调用,用于初始化实例属性。

属性赋值过程及其先后顺序:
赋值的位置:
① 默认初始化(类属性的默认初始化在类加载过程的link阶段的prepare子阶段,实例属性的默认初始化在创建对象时的分配内存阶段之后。)
② 显式初始化- 定义时赋值或者在代码块中赋值
(类属性的显示初始化在类加载过程的初始化阶段,调用方法;实例属性的显示初始化在对象创建完成后的初始化阶段执行。)
③ 构造器中初始化(执行完实例属性显示初始化之后,调用方法再次初始化)
④ 通过“对象.属性“或“对象.方法”的方式赋值
赋值的先后顺序:① - ② - ③ - ④

加载原则:先父类再子类,先静态后非静态,先代码块后构造器。
注:在类加载的prepare阶段只会为类变量在静态域上分配空间并默认初始化(1.7之后静态域被移到了堆内存)。
在对象创建过程中才会为实例变量在堆空间上分配空间并默认初始化。

2.4 代码块

代码块也被称为初始化块,与显示初始化同级,先于构造器。

代码块(或初始化块)的作用: 对Java类或对象进行初始化
代码块的可用修饰符:static default
代码块(或初始化块)的分类: 一个类中代码块若有static修饰,称为静态代码块(static block);没有使用static修饰的,为非静态代码块。
static代码块通常用于初始化static的属性。

静态代码块:用static 修饰的代码块
1. 可以有输出语句。
2. 可以对类的属性、类的声明进行初始化操作
3. 不可以调用非静态的属性和方法。
4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
5. 静态代码块的执行要先于非静态代码块。
6. 静态代码块随着类的加载而加载,且只执行一次
非静态代码块:没有static修饰的代码块
1. 可以有输出语句。
2. 可以对类的属性、类的声明进行初始化操作。
3. 除了调用非静态的结构外,还可以调用静态的变量或方法
4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
5. 每次创建对象的时候,都会执行一次。
6. 且非静态代码块先于构造器执行

程序中成员变量赋值的执行顺序
声明成员变量的默认初始化
显式初始化、多个初始化块依次被执行同级别下按先后顺序执行)
构造器再对成员进行初始化操作
通过”对象.属性”或”对象.方法”的方式,可多次给属性赋值

new 一个子类对象的过程
先执行类的加载过程:由父及子 静态先行;先初始化父类和子类的静态属性。
再执行创建对象过程:由父及子 显示初始化/代码块先于构造器。父类的代码块、构造器整体执行完之后,才会执行子类的显示初始化/代码块、构造器。
确认父类、子类有多个构造器时的执行顺序,只需确认构造器的调用逻辑即可,无super(参数列表)时,默认访问super(空)。具体如下:
1、先加载静态代码块,且只会执行一次,再new对象时不再执行:
先父类静态代码块
再子类静态代码块
2、先显示初始化/代码块,后构造器;先父类再子类,每次new对象时都会执行:
再父类 显示初始化/非静态代码块、父类构造器
再子类 显示初始化/非静态代码块、子类构造器

例:

class Father {
    static {
        System.out.println("11111111111");
    }
    {
        System.out.println("22222222222");
    }
    public Father() {
        System.out.println("33333333333");
    }
}

public class Son extends Father {
    static {
        System.out.println("44444444444");
    }
    {
        System.out.println("55555555555");
    }
    public Son() {
        System.out.println("66666666666");
    }


    public static void main(String[] args) { // 由父及子 静态先行
        System.out.println("************************");
        new Son();
        System.out.println("************************");
        new Son();
        System.out.println("************************");
        new Father();
    }

}
console:
//调用main()前先加载当前类,类加载过程:由父及子 静态先行
11111111111
44444444444
************************
//创建实例new Son(),创建对象过程:由父及子 代码块先于构造器
22222222222
33333333333
55555555555
66666666666
************************
//静态代码块随着类的加载而加载,且只执行一次;非静态代码块在每次创建对象的时候,都会执行一次
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333

Process finished with exit code 0

例2:

class Root{
    static{
        System.out.println("1 Root的静态初始化块");
    }
    {
        System.out.println("4 Root的普通初始化块");
    }
    public Root(){//4
        System.out.println("5 Root的无参数的构造器");
    }
}
class Mid extends Root{
    static{
        System.out.println("2 Mid的静态初始化块");
    }
    {
        System.out.println("6 Mid的普通初始化块");
    }
    public Mid(){//3
        System.out.println("7 Mid的无参数的构造器");
    }
    public Mid(String msg){//2
        //通过this调用同一类中重载的构造器
        this();//3
        System.out.println("8 Mid的带参数构造器,其参数值:"
                + msg);
    }
}
class Leaf extends Mid{
    static{
        System.out.println("3 Leaf的静态初始化块");
    }
    {
        System.out.println("9 Leaf的普通初始化块");
    }
    public Leaf(){//1
        //通过super调用父类中有一个字符串参数的构造器
        super("hello"); // 2
        System.out.println("10 Leaf的构造器");
    }
}
public class LeafTest{
    public static void main(String[] args){
    //new一个对象时的构造器调用逻辑:public Leaf() -》 super("hello") -》public Mid(String msg) -》this()
    //-》public Mid()-》super()-》public Root()
        new Leaf();
        System.out.println("****************************");
        new Leaf();
    }
}

2.5 内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。Inner class的名字不能与包含它的外部类类名相同;

成员内部类:
作为类的成员:
和外部类不同,Inner class还可以声明为private或protected;
可以调用外部类的结构;
Inner class 可以声明为static。
作为类:
可以在内部定义属性、方法、构造器等结构,非static的成员内部类中的成员不能声明为static的。
可以声明为abstract类 ,因此可以被其它的内部类继承
可以声明为final的
编译以后生成OuterClass$InnerClass.class字节码文件
外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式,成员内部类可以直接使用外部类的所有成员,包括私有的数据;当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的。
成员内部类(static成员内部类和非static成员内部类)

局部内部类(不谈修饰符):
定义在方法或者代码块中,只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。局部内部类可以使用外部类的成员,包括私有的。 局部内部类可以使用外部方法的局部变量,但是必须是final的。局部内部类和局部变量地位类似,不能使用public,protected,缺省,private。局部内部类不能使用static修饰,因此也不能包含静态成员。

匿名内部类
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。
一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,通常作为方法的实参。

格式:

new 父类构造器(实参列表)| 实现接口(){
			//匿名内部类的类体部分
} 

匿名内部类的特点:
匿名内部类必须继承父类或实现接口;匿名内部类只能有一个对象;匿名内部类对象只能使用多态形式引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值