JAVA学习笔记随记3(面向对象高级)
类变量
类变量的内存布局
目前对于类变量的内存布局不能一概而论。
对于jdk8及其之前的版本,类变量放在方法区的静态域中。
对于之后版本的jdk而言,类变量放在堆区。
但实例化出的对象,类变量都是通过引用的。
无论如何有以下两个公示:
1.静态对象被所有对象共享。
2.static类变量,在类加载的时候就生成了。
类变量的使用细节
1.什么时候使用类变量?
当需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量。
2.类变量和实例变量的区别
类变量是该类的所有对象所共有的,而实例变量是每个对象独享的。
3.加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量。
4.类变量可以通过类名.类变量名,或对象名.类变量名来访问,但推荐使用类名.类变量名。
5.实例变量不能通过类名.类变量名访问。
6.类对象是在类加载时就初始化,故无创建对象时也可以使用,并随类消亡而消亡。
类方法
类方法和类变量大体是类似的,但关于方法上还是有一些小区别的。
1.类方法和类变量都是随着类加载而加载的,类变量可能存放在堆区或方法区的静态域中,但类方法是一定存放在方法区的。
2.类方法中无this和super参数。其实这个挺好理解的,this实质上就是实例化的对象的地址,而类方法不依赖于对象,所以类方法无this参数。而super也是和对象有关的关键字,相当于父类中的this。
3.类方法只能访问类变量和类方法。理由同上。
main方法
深入理解main方法,main方法形式为:public static void main(String[] args)
1.JAVA虚拟机需要调用类的main方法,所以该方法的访问权限必须使public。
2.JAVA虚拟机在执行main()方法时,不必创建对象,所以必须时static。
3.该方法接受String类型的数组参数,该数组保存执行JAVA命令时传递给所运行的类的参数。
特别提示:
由于main方法是静态方法,所以不能调用本类中非静态属性或方法,必须通过实例化出对象才能调用本类中的非静态成员。
代码块
基本介绍
代码块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,用{}包围。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,而是加载类时,或创建对象时隐式的调用。
基本语法
[修饰符]{
代码
};
注意:1.修饰符可选可不写,写的话只能写static.
2.代码块分为两类,加static为静态代码块,没有static修饰为普通代码块。
3.';'可以写上,也可以省略。
注意事项和细节
1.static代码块称为静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果时普通对象,没创建一个对象就执行。(静态代码块就是将类加载进方法区时才执行,而普通代码块则是在堆区创建对象时才执行,两者服务的对象不同,一个是类,一个是对象。)
2.类什么时候被加载?
(1)创建对象实例时。
(2)创建子类对象实例,父类会优先加载。
(3)使用类的静态成员时。
类加载就是将类放入方法区,便于以后使用,所以在第一次用到该类的东西时,该类就会被加载,并且只会被加载一次,当有继承关系时,总是要寻根溯源,优先加载父类。
3.普通代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。如果知识使用类的静态成员或方法,普通代码块并不会执行,因为并没有涉及堆区对象的事情。
4.创建一个对象时,在一个类中,调用顺序是:
(1)调用静态代码块和静态属性初始化(同级按定义顺序);
(2)调用普通代码块和普通属性初始化;
(3)调用构造器。
5.构造器的最前面其实隐含了super()和调用普通代码块的语句,所以两者优先于当前构造器。
6.当一个子类对象被创建时,父类,子类的静态代码块,普通代码块,静态属性,普通属性,构造器的调用顺序如下:
首先是将父类和子类加载,而加载时先加载父类。
(1)父类的静态代码块和静态属性初始化。
(2)子类的静态代码块和静态属性初始化。
之后就是堆区的事情了,还是先父类再子类。
(3)父类的普通代码块和普通属性初始化。
(4)父类的构造器。
(5)子类的普通代码块和普通属性初始化。
(6)子类的构造器。
7.静态代码块只能调用静态成员,普通代码块可以调用所有成员。
静态就是类本身固有的属性,所以只能在类这一层面调用自己的资源,而普通的属于对象层面,属于个性的实例,所以可以调用所有的成员。
final关键字
final中文意思是最后的,在这里解释了不可修改的更佳。
final可以修饰类,方法,属性,局部变量。
以下位final的使用场景:
1)当不希望类被继承时,可以使用final修饰。
2)当不希望父类的某个方法被子类重写时,可以使用final修饰。
3)当不希望某个属性被修改时,可以使用final修饰。
4)当不希望某个局部变量被修改时,可以使用final修饰。
总之,用final修饰的成员就不可被修改了。在不同的成员有具体不同的体现。
final使用注意事项:
1.final修饰的属性又叫常量,一般用XXX_XXX_XXX来命名,全大写。
2.final修饰的属性在定义时必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:
(1)定义时,赋值。如:public static double TAX_RATE=0.08
(2)在构造器中。
(3)在代码块中。
其实就是按照实例一个对象时赋值的顺序。
3.若final修饰的属性是静态属性,则初始化位置只能是
(1)定义时.
(2)在静态代码块中。
静态属性,赋值只有定义和静态代码块中,第一次加载类。
4.final类不能继承,但可以实例化对象。
5.如果类不是final类,但是含有final方法,则该方法虽然不能重写,但却可以继承,子类使用。
6.如果类已经用final修饰了,就没有必要再用final修饰方法了。
因为方法只有继承才有重写之说,不能继承了,何来重写?
7.final常和static搭配使用,效率更高,不会导致类的加载,底层编译器做了优化处理。
8.包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
抽象类
基本介绍:
当父类的一些方法不确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,同时必须用abstract修饰该类,这个类就是抽象类。
格式
抽象类
访问修饰符 abstract 类名{
}
抽象方法
访问修饰符 abstract 方法命(参数列表);//没有方法体
使用细节
1.抽象类不能被实例化。
因为抽象类只是定义了方法名,没有方法体,所以不能实例化去使用其方法。
2.抽象类不一定有抽象方法,有抽象方法的类一定是抽象类。
抽象的人不一定有抽象的特征,但由抽象特征的人一定很抽象。
3.abstract只能用来修饰方法和类,不能用来修饰其他成员。
4.抽象类的本质还是类,所以可以拥有类可以拥有的所有成员。
5.如果一个类继承了一个抽象类,则它必须实现抽象类的所有抽象方法,或者它本身也定义成抽象类。
6.抽象方法不能和private,final和static修饰,因为这和重写违背了。
关于静态方法,静态属性而言,他们都是类相关的,当子类和父类同名时,只是父类的静态属性和方法被隐藏了,而重写则是在访问时,子类方法的优先级高于父亲,而隐藏则无优先级之分。所以静态的成员,无法被重写。
至于非静态属性,也只是被继承了,和上述两者一样,被隐藏而非被重写,且在使用时,根据编译类型输出。
接口
基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况将这些方法写出来。
语法:
interface 接口名
{
//属性
//方法
}
class 类名 implements 接口
{
//自己属性
//自己方法
//必须实现的接口的抽象方法
}
PS:
1.jdk7.0前,接口里的所有方法都没有方法体
2.jdk8.0之后接口类可以有静态方法,默认方法,也就是可以有方法的实现。
注意事项和细节
1.接口不能被实例化。和抽象类类似,因为并没有具体实现的方法体,所以并不能实例化对象。
2.接口中的所有方法都是public方法,不写也是public,且只能是public,接口中抽象方法可以不加abstract修饰。
3.一个普通类实现接口,就必须实现该接口的所有方法,和抽象类类似。
4.抽象类实现接口,可以不用实现接口的方法。但当普通类继承抽象类时,还得实现该接口的方法,就相当于将接口的方法放入抽象类了,一推再推,总得有人去实现。
5.一个类同时可以实现多个接口。和抽象类对比的话,类的继承只能是单继承。
6.接口中的属性,默认为public static final修饰的,不容修改,且只能在定义时初始化。
7.接口不能继承类,但却可以继承其他别的接口。
8.接口的修饰符只能时public和默认,这点和类的修饰符类似。
接口VS继承
解决的问题不同
继承的价值在于:解决代码复用性和可维护性
接口的价值在于:设计,设计好各种规范,让其他类去实现这种方法。
接口比继承更加灵活
继承时is-a的关系,而接口只需满足like-a的关系。
接口在一定程度上实现了代码解耦(即接口规范性+动态绑定机制)
接口的多态特性
1)多态参数
2)多态数组
3)多态传递
内部类
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类。是类的第五大成员(属性,方法,构造器,代码块)。内部类的最大特点便是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
基本语法
class Outer{//外部类
class Inner{//内部类
}
}
class Other{//外部其他类
}
内部类的分类
定义在外部类局部位置上(比如方法内):
1)局部内部类(有类名)
2)匿名内部类(没有类名,重点!!!)
定义在外部类的成员位置上:
1)成员内部类(没有static修饰)
2)静态内部类(使用static修饰)
局部内部类
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名
1.可以直接访问外部类的所有成员,包括私有的。(都是内部类了,说明一定在类中啊,private都限制不了。)
2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final。
3.作用域:仅仅在定义它的方法或代码块中。
4.外部类访问内部类成员,创建对象再访问。
5.外部其他类不能访问局部内部类,因为局部内部类就是一个局部变量。
6.如果外部类和局部内部类的成员重名时,默认就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
记住:1.局部内部类定义在方法/代码块中
2.作用域在方法/代码块中。
3.本质仍然是一个类。
匿名内部类
1.匿名内部类常见为基于接口或基于类的匿名内部类,本质上还是一个实现接口的类或一个子类。
2.匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上来说,既有定义类的特征,也有创建对象的特征。
语法如下:
直接当成对象去使用
new 父类名{
//重写父类方法
//例如
public void cry
{
}
}.cry();
直接当成类去使用
父类名 对象名=new 父类名(){
//重写父类方法
//例如
public void cry
{
}
};
对象名.cry();
其余的一些特征,和局部内部类类似,就相当于一个局部变量的作用。
成员内部类
其实学过以上两个内部类之后,大概就能理解了内部类是一个什么样的成分。
对于成员内部类而言,而以上两个的不同点在于,所处的位置是成员而已,所以外部类和内部类的交互基本上没什么区别。把它当成外部类的一个成员即可。
对于外部其他类而言,和之前的就不同了,因为外部其他类是可以访问内部类的,方法便是可以直接连续两次new,也可以先创建一个外部类的对象,再new内部类。
外部类名 对象名=new 外部类名().new 内部类名();
外部类名 对象名1=new 外部类名();
外部类名 对象名2=对象名1.new 内部类名();
对了,成员内部类和局部内部类的区别还有就是可以加上一些访问修饰符去限制访问,因为它的地位就是一个成员。
静态内部类
就是成员内部类用static修饰,和成员内部类相比,考虑上静态的很多特性即可。
完结撒花!