三个修饰符的作用:提高程序运行性能,优化程序结构
static
static表示"静态" ,“共享”, “全局”, “属于类的” 只能修饰方法和变量和内部类(相当于外部类)
1.修饰成员变量
1.1 引用成员变量的方式建议使用className.var。
1.2 表示类的所有实例共享此变量且可以修改,并且继承A的子类的实例也可以修改变量
用法:
class A{
//非静态成员变量每new出一个实例就在实例的堆区域拷贝一份,互不影响
String name;
//静态成员变量属于类,在编译时就将此变量
static int age;
//类的方法存在静态存储区(包含类的定义等信息),每个实例在堆里有一个方法的指向,当age使用static修饰时,jvm会将静态变量存放至静态存储区
void M1(){};
void M2(){};
static void main(args){
A a= new A();
}
例子:
public class staticA {
static int a=0;
int b=1;
void M1(){
System.out.println("a:"+a);
}
public static void main(String args[]){
staticA a= new staticA();
a.M1();
a.a=2;
a.M1();
}
}
测试:
public class teststaticA extends staticA{
public static void main(String args[]){
teststaticA a=new teststaticA();
a.M1();
a.a=10;
a.M1();
teststaticA.a=11;
a.M1();
}
}
结果:
a:0
a:10
a:11
从内存优化角度看,上面的方法虽然优化了内存,但有个问题,就是每个对象对静态成员变量修改会影响其他对象,可以将成员变量设置成private,在类的构造函数对变量操作,例如
class A{
//类外部无法访问此变量
private static int i;
A(){
//每实例化一个对象值自增,A所有实例对象共享此变量
i++;
}
}
2.修饰成员方法
类的方法本来即是存放在类定义中的(内存静态存储区域),static修饰成员方法可以使用classname.method(),避免了new对象的资源消耗。
2.1 static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
2.2 静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字
2.3 静态的方法可以被继承,但是不能重写。如果父类中有一个静态的方法,子类也有一个与其方法名,参数类型,参数个数都一样的方法,并且也有static关键字修饰,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。
父类
public class father {
int i;
public static void m2() {
System.out.println("m2");
}
}
子类
public class child extends father {
public static void main(String args[]) {
child child = new child();
child.m1();
}
public static void m2(){
System.out.println("子类的静态同名方法隐藏了父类m1");
}
}
3.修饰代码块
静态代码块是独立于类成员的语句块,不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次,初始化时首先初始化静态块然后普通类成员初始化然后是静态方法(classname.mthod方式调用静态方法则会在实例初始化完成之前执行),最后是实例
例子:
class Book {
public Book(String msg) {
System.out.println(msg);
}
}
public class staticblock {
Book book1 = new Book("book1成员变量初始化");
// static {或者这样写
// book2 = new Book("static成员book2成员变量初始化");
// book4 = new Book("static成员book4成员变量初始化");
// }
// 静态代码块
static Book book2 = new Book("static成员book2成员变量初始化");
public staticblock(String msg) {
System.out.println(msg);
}
Book book3 = new Book("book3成员变量初始化"); // 静态代码块
static Book book4 = new
Book("static成员book4成员变量初始化");
public static void funStatic() {
System.out.println("static修饰的funStatic方法");
}
public static void main(String[] args) {
staticblock.funStatic();
System.out.println("****************");
staticblock p1 = new staticblock("p1初始化");
}
}
4.静态导包
package com.a.b;
public class c{
public static void m(Object o){
System.out.println(o);
}
}
采用static导入包后,在不与当前类的方法名冲突的情况下,无需使用“ 类名.方法名”的方法去调用类方法
//import all method
import static com.a.b.c.*;
public class t
{
public static void main( String[] args )
{
m("Hello World!");
}
/**Output
* Hello World!
}
final
final修饰符表示“终态”“不可修改”的含义,可以修饰类,成员变量,方法(锁定方法)。
1.修饰类
final修饰的类不可以被继承,finalclass表示这个类不会再被改变,也不允许其他类进行操作,不希望有子类,final类没有可扩展性。
final类成员方法都会被默认指定final关键字,成员变量可以自己定义。
2.修饰方法
final把方法锁定,防任何继承类修改它的含义,final方法可以被继承不可被重写/覆盖。
类的private方法会隐式地被指定为final方法。
例子
public class father {
int i;
public void m1() {
System.out.println("m1");
}
public final void m3() {
System.out.println("m3");
}
}
子类
public class child extends father {
public static void main(String args[]) {
child child = new child();
child.m3();
}
public void m1(){
System.out.println("子类覆盖了了父类的普通m1方法");
}
public void m3(){
System.out.println("程序报错,无法覆盖父类final方法");
}
}
3.修饰变量。(常量的含义)
3.1 对于一个final变量,如果是基本数据类型(int char String等)的变量,则其数值一旦在初始化之后便不能更改(每个实例的变量而言); final作用于类的成员变量时,类成员变量(局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,旦被初始化赋值之后,就不能再被赋值。
例子
class test{
final int i=0;
final String="aaa";
void test(){
i=2;//error
final Object obj=new Object();
obj=new Object();//error
}
void m1(){
final int a;//可以在使用的时候赋值
}
}
如果没有在初始化时赋值则可以在构造函数赋值
public class father {
final int i;
public father(int b){
i=b;
}
public void m1(int a) {
System.out.println("m1:"+i);
final int aa = a;
System.out.println("m1:"+aa);
}
public static void m2() {
System.out.println("m2");
}
public static void main(String args[]){
father father=new father(77);
father.m1(88);
father father1=new father(66);
father1.m1(99);
}
}
3.2 final变量是个常量,编译器会把它当做编译期常量使用使用到变量的地方会直接替换,非final变量则是在运行时通过链接访问。
例子
public class testvar {
public static void main(String[] args) {
String a = "hello2"; //对象引用变量a放在栈中,字符串放在字符串池,a指向它
String f="hello2";//f和a指向一个地址所以相等
System.out.println(a==f);//true
String aa=new String("hello2");
String ff=new String("hello2");
System.out.println(aa==ff);//flase
final String b = "hello";//常量
String c = b + 2; //常量,c指向字符串池的hello2
System.out.println((a == c));//true
String d = "hello";//d指向hello
String e = d + 2;//字符串对象相加会生成新的字符串对象,而不是查找字符串池,所以对象的引用不同
//==对基本类型的变量比较的是值,引用类型的变量存储的并不是 “值”本身,而是于其关联的对象在内存中的地址
System.out.println((a == e));//false
}
}
3.3 如果是引用类型的变量(maplist),则在对其初始化之后便不能再让其指向另一个对象,但对象内容可变。
public class testvar {
final List<String> list=new LinkedList<String>();
public static void main(String[] args) {
testvar r=new testvar();
r.list=new LinkedList();//error
r.list.add("a");
r.list.add("b");
Iterator<?> i=r.list.iterator();
while(i.hasNext()){
System.out.println(""+i.next());
}
}
static-final
static作用于成员变量用来表示只保存一份副本(内存栈中)每个对象共享这个变量,而final的作用是用来保证变量不可变(每个对象的变量值可以在实例化的时候赋值,每个对象的变量值可以不同)
例子
public class testfinal {
public static void main(String[] args) {
MyClass myClass1 = new MyClass();
System.out.println(myClass1.i);
System.out.println(myClass1.j);
System.out.println(myClass1.k);
myClass1.i++;//error.每个实例可以保存有不同的final变量值,但一旦赋值无法修改
myClass1.j++;//每个对象都可对static的变量修改
myClass1.k++;//error 实例化赋值之后,实例无法对final修饰的成员变量修改
MyClass myClass2 = new MyClass();//成员变量j的值是myclass1实例化后赋的的值
System.out.println(myClass2.i);
System.out.println(myClass2.j);
System.out.println(myClass2.k);
}
}
class MyClass {
public final double i = Math.random();
public static double j = Math.random();//MyClass实例化后得到的j值保存在栈中,被所有实例共享
public static final double k = Math.random();
//public static double j ;
public MyClass(){
//j = Math.random();//每个实例化都会改变j值,并且这个j值会被Myclass的所有实例共享
}
}
static final static-final变量初始化机制
static变量在类加载的时候就会赋值,如果是static int a;则会在静态区给一个初始值0,之后可以修改,不能通过构造方法或非静态块来进行初始化,从设计角度来考虑,如果可以通过构造方法或非静态块来进行初始化,那每new一个对象都会对静态的常量进行再一次的赋值操作,也就是说修改它的值,
final变量在实例化的时候赋值即可,类加载的时候不会赋初始值,final int a;是个空final必须要实例化的时候赋值才能使用
例子
public class testmemberinnerclass {
private final int a;//空final
private static int b;
private static final int c;//final修饰的不会初始赋值
static {
c=0;
}
public testmemberinnerclass(int j,int l){
a=j;
System.out.println("构造a:"+a);
b=l;
System.out.println("构造b:"+b);
}
public class innerclass {
void m2_inner(int i, int k) {
b = k;
System.out.println("inner class a:" + a);
System.out.println("inner class b:" + b);
}
}
public static void main(String args[]) {
System.out.println(""+b);
System.out.println("对象outer1");
testmemberinnerclass outer = new testmemberinnerclass(10,11);
innerclass inner = outer.new innerclass();// 必须通过包含内部类的外部类去访问内部类
inner.m2_inner(4, 5);
System.out.println("对象outer2");
testmemberinnerclass outer1 = new testmemberinnerclass(20,21);
innerclass inner1 = outer1.new innerclass();// 必须通过包含内部类的外部类去访问内部类
inner1.m2_inner(4, 5);
}
}
结果:
0
对象outer1
构造a:10
构造b:11
inner class a:10
inner class b:5
对象outer2
构造a:20
构造b:21
inner class a:20
inner class b:5