一)static修饰符
1、静态变量与实例变量的区别?
1)静态修饰的成员:属于类级别的成员,是全体类实例(所有对象)所共享。静态成员随着类的加载而加载,加载于方法区中,且优先于对象存在。
2)静态属性:只有一份(而实例变量是每个对象有一份),全体实例共享,类似于全局变量。使用类名访问静态变量,以及类名直接调用方法,不需要创建对象。
2、什么时候使用static修饰符?它有什么用?
静态的,只能在类内部使用,可以修饰:属性,方法,内部类。在类加载期间初始化,存在方法区中。
3 、是否可以从一个static方法内部发出对非static方法调用 ?
修饰的变量:
1.属性 --> 被static关键字修饰的属性叫类变量,是全类共有的,可直接用类名访问。初始化类变量是在类加载是完成。
类加载:当虚拟机JVM第一次使用一个类时,会通过classpath找到这个类所对应的字节码文件,将这个文件中包含的类信息全读入JVM并保存起来,使下次再用这个类时就不需要再去加载,一个类只加载一次。
2.方法 --> 被static关键字修饰的方法叫静态方法,可直接用类名访问。
静态方法的特点:
(1)静态方法只能访问类的静态成员(静态属性和静态方法);非静态方法既可访问静态,也可访问非静态。
(2)静态方法只能被子类中的静态方法覆盖,并且没有多态;
例子:A a=new B(); A类是B类的父类(多态)
A中有m()方法,B中也有m()方法,则a.m()调用的是B中的m()方法;
如果A、B中的m()方法都是静态方法,则a.m()调用的是A中的m()方法
(3)静态方法中没有隐含参数this,因此不能访问当前对象资源。也不能定义this和super关键字,因为静态优于对象先存在。
非静态方法省略的是this,静态方法省略的是类名(在同一类中),即直接使用属性和方法。
静态方法一般用于与当前对象无关工具方法,工厂方法。如:Math.sqrt() Arrays.sort()
3.静态代码块
随着类的加载而执行(用到类的内容才叫加载,只有引用是不加载的),且只执行一次,且优先于主函数,用于给类初始化。
代码块(构造代码块):给所有对象进行统一初始化,且优先于构造器执行;而构造器是给对应的对象进行初始化。
public class GOO {注:对象的创建过程以及顺序
{ //代码块(构造代码块),在创建对象时候执行!类似于构造器的作用
System.out.println("HI");
}
//静态代码块,在类的加载期间执行,只执行一次
static{
System.out.println("Loading Goo.class");
}
}
public static void main(String[] args) {
Point p1 = new Point(3,4);
Point p2 = new Point(6,8);
//在对象上调用方法,当前对象隐含传递给隐含参数this
System.out.println(p1.distance(p2));//distance(p1,p2)
double d = Point.distance(p1, p2);
System.out.println(d); //5
//静态方法调用时候不传递隐含的当前对象参数 }
class Point{
int x; int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
//静态方法中没有隐含参数this!在静态方法中不能访问this的属性和方法!
public static double distance(Point p1, Point p2){
int a = p1.x -p2.x;
int b = p1.y -p2.y;
return Math.sqrt(a*a + b*b); }
/** 计算当前点(this)到另外一个点(other)的距离 */
public double distance(/*Point this*/Point other){
int a = this.x - other.x;
int b = this.y - other.y;
double c = Math.sqrt(a*a + b*b);
return c;
}
}
Person P = new Person( “chang” , 23) ; 这句话都做了什么事情?
①因为new用到了Person.class,所以会先找到Person.class文件加载到内存中。
②执行该类中的static代码块(如果有的话),给Person类.class类进行初始化。
③在堆内存中开辟空间,分配内存地址,栈内存中开辟空间也就有了。
④在堆内存中建立对象的特有属性,并进行默认(隐式)初始化。
⑤对属性进行显式初始化。
⑥对对象进行构造代码块初始化。
⑦对对象进行对应的构造器初始化。
⑧将内存地址赋给栈内存中的P变量。
二)fianl修饰符
1、fianl、fianlly与fianlize有什么区别?
fianl适用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖,类不可被继承。
finally是异常处理语句中的一部分,表示总是要执行的代码块;
finalize是Object类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件。
2、fianl有什么用?什么时候使用fianl?
使用final方法,出于对两方面的考虑。第一个是为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
第二个理由是程序执行的效率。将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受到到不到嵌入代码所带来的任何性能提升。因为任何提升都被花在方法内部的时间抵消了。Java编译器能自动侦测这些情况,并颇为“明智”地决定是否嵌入一个final方法。然而,最好还是不要完全相信编译器能正确地作出所有判断。
通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。
类内所有private方法都自动成为final。由于我们不能访问一个private方法,所以它绝对不会被其他方法覆盖(若强行这样做,编译器会给出错误提示)。可为一个private方法添加final指示符,但却不能为那个方法提供任何额外的含义。
3、可以修饰:
1)变量 --> 被final修饰的变量成为常量,一旦赋值,不能被修改。
2)方法 --> 如果一个方法被final修饰,说明该方法不能被子类覆盖
3)类 --> 如果一个类被final修饰,说明该类不能被继承
4、注意:final使得被修饰的变量 "不变 ",但是由于对象型变量的本质是“引用”,使得“不变”也有了两种含义:引用本身的不变,和引用指向的对象不变。
1)引用本身的不变:
final StringBuffer a=new StringBuffer( "immutable ");
final StringBuffer b=new StringBuffer( "not immutable ");
a=b;//返回值为false
2)引用指向的对象不变:
final StringBuffer a=new StringBuffer( "immutable ");
a.append( " broken! "); //编译通过
可见,final只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的“值”相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。
final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。其中一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它“永远不变”,但其实那是徒劳的。
三)abstract修饰符
可以修饰的变量:
1.类 --> 不能创建对象,但可以声明对象的引用
抽象就是将拥有共同方法和属性的对象提取出来,提取后,重新设计一个更加通用、更加大众化的类,就叫抽象类。
1)抽象类可以有具体的方法,或者全部都是具体方法,但一个类中只要有一个抽象方法,那么这个类就是抽象类,并且必须用abstract修饰类。
2)抽象类可以被继承,则子类必须实现抽象类中的全部抽象方法,否则子类也将是抽象类。抽象类也可主动继承实体类。
3)抽象类不能实例化,即不能用new生成实例;但可以声明一个抽象类型的变量并指向具体子类的对象。
4)抽象类可以实现接口中的方法,抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
2.方法 --> 只有定义,没有实现
(1)如果一个类中有抽象方法,则这个类必须是抽象类
(2)子类继承一个抽象类,如果子类不希望也成为抽象类,
就必须实现父类中所有的抽象方法
(3)抽象类中有构造方法
3、abstract class和interface有什么区别?
①从语法角度:abstract class方法中可以有自己的数据成员,也可以有非abstract的成员方法,并赋予方法的默认行为,而在interface方式中一般不定义成员数据变量,所有的方法都是abstract,方法不能拥有默认的行为。
②从编程的角度:abstract class在java语言中表示的是一种继承关系,一个类只能使用一次继承关系。而一个类可以实现多个interface。
③从问题域角度:abstract class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。