Java 基础知识(一)

final 关键字

使用final关键字的三种情况:数据、方法、类

final 数据

final数据的情况:

  • 1、永不会改变的编译时常量

    • 在编译的时候编译器将其带入到用到它的计算式子中,这可以减少运行时的负担。
    • JAVA中的这类常量必须是基本数据类型,并且必须使用final关键字,必须在使用之前赋值。
    • 一个既是static又是final的域只占据一段不能改变的内存空间
    • static final和final没多大区别
    • 带有final static 的基本类型都有全大写命名,单词之间用下划线连接
  • 2、在运行时被初始化,然后不再被改变

    • 当对对象引用,而不是基本的数据类型时,final使得引用恒定不变,一旦引用初始化指向一个对象,则一直指向该对象,但是具体的对象内容是可以改变。
    • 这样的限制也适用于数组,数组也是对象。所以用final修饰的数组名,初始化之后就不能在指向其他的数组了,但是可以修改数组中的元素值。
    • static final:在程序进行装在的时候就会存在某一个指定内存空间中,对于该类的对象来说,用这个两个关键字修饰的变量是唯一的,并不会在类创建的时候创建。
    • final:是在类创建的时候创建,所以同一个类的不同对象的值是不一样的。

    从上面可以看出,让基本数据类型成为final比让引用成为final更有意义

空白final

指被申明为final但是又没有给定初始值的域。根据前面第1条中知道,必须在使用前对final域进行初始化,所以,在该类的构造器中必须进行初始化。这样就可以让一个类中的final域的值根据不同的对象而不一样。如下面的代码所示:


 public class BlankFinal {

    private final int i = 1;//常量 初始化
    private final int j;//常量 空final
    private final Peppet peppet;//引用 空final
    //以上两个变量在没有进行构造函数初始化的时候会一直报错

    public BlankFinal(int j) {
        this.j = j;//初始化之后上面的空白final就会没有出错了
        this.peppet = new Peppet(j);
    }

    public static void main(String[] args) {
        BlankFinal bf =new BlankFinal();
//        bf.i++;//出错,不能修改了
        bf.peppet.i = 3;
    }
}

class Peppet {
    public int i;
    public Peppet(int i) {
        this.i = i;
    } 
}

final参数

当指定传入的参数是final的类型时,如果参数是引用类型,则在方法中不能修改参数所指向的引用,如果是基本数据类型,则不能修改参数的值。代码如下:

public class FinalParam {

    void with(final Demo demo) {
//        demo = new Demo();//报错,不能修改
    }

    void without(Demo demo) {
        demo = new Demo();//能修改
    }

    int f(final int i) {
//        return i++;//出错,i不能改变
        return i+1;
    }
}

class Demo {
    public void test(){}
}

final方法

使用final方法的原因

  • 1、第一原因是把方法锁定。

    可以在将一个方法见面加上关键字final,这样就可以防止任何继承类来修改这个方法。这样可以确保在继承中使用该方法的行为不会改变,并且不会被覆盖。

  • 2、第二个原因是效率问题

    以前可以通过给方法加上final,在编译器编译的时候就会将代码的副本代替到调用的地方,这样可以在一定程度上减少方法调用带来的开销,从而提高效率。

    问题:当方法体很长的时候,这样就会使得代码变得很膨胀,可能就会看不到内嵌代码所带来的任何性能上的提升。

    结论:在现在的JDK中可以让编译器和JVM去处理代码的效率问题,只有当需要明确的禁止将方法进行覆盖的时候,才将该方法设置为final。

final和private关键字

一个类中的private方法实际上都隐式的指定了是final的,由于子类无法获取到private方法,所以也就无法覆盖private方法了,如果对一个private方法再添加final关键字是不起任何作用的。

覆盖:只有在某个方法是其基类的接口中的一部分的时候,才会出现覆盖。如果某个方法是private,那该方法就不是基类中接口的一部分,这样的方法不能被子类继承的,即使子类中有相同方法名的方法,那也只是方法名相同而已。

final类

当定义一个类为final类的时候,表明该类不允许被继承,换句话说,该类不会有任何的子类。由于final类是不允许被继承的,所以final类中所有的方法都被隐式的加上了final关键字,final类不会影响该类中其他的域是否被指明为final。

static关键字

应用的场景

  • 1、只为某一个特定域创建单一的内存空间,不需要考虑创建对象的问题
  • 2、希望某个方法不与包含它的任何对象关联在一起,即不创建对象也能调用该方法
  • 3、使用static关键字修饰静态代码块
  • 4、在使用import导包的时候,在import之后加上static关键字,就是静态导包

使用static

只需要将static放在定义的域或者方面前面,即可以将该方法或者域定义为static类型。当一个域或者方法被申明为static的时候,就意味着这个域或者方法不会与包含它的任何对象相关联。所以,即使从未创建过某个类的对象,也可以调用该类的static域或者方法,在有些地方将这种static的方法或者域成为类数据和类方法。

在使用static域或者方法的时候,既可以直接使用ClassName.StaticDomainOrMethod,也可以像其他普通的域或者方法一样,通过对象来进行访问。

Note:
每一个类的代码程序代码在什么时候被加载?

  • 1、在创建类的第一个对象时会加载该类的程序代码
  • 2、当访问该类的静态域或者静态方法时也会加载

实际上构造器也是一个static方法,所以更准确的来说,类是在任何static成员被访问的时候加载的。

继承和初始化

首先看看如下的代码:

public class StaticInit extends Insect{

    private int k= printInit("StaticInit.k initialized");

    public StaticInit() {
        System.out.println("k="+k);
        System.out.println("j="+j);
    }

    private static int x2 = printInit("static StaticInit.x2 initialized");

    public static void main(String[] args) {
        System.out.println("StaticInit Constructor");
        StaticInit staticInit = new StaticInit();
    }


}

class Insect {
    private int i=9;
    protected int j;

    public Insect() {
        System.out.println("i="+i+" , j="+j);
        j = 39;
    }

    private static int x1 = printInit("static Insect.x1 initialized");

    static int printInit(String s) {
        System.out.println(s);
        return Math.round(10);
    }
}

流程说明:

  • 1、首先是找到StaticInit.main()方法,于是开始加载StaticInit类,在加载StaticInit的时候,发现该类继承自Insect类,于是就来到Insect类中开始对该类中static域进行初始化,然后就会到StaticInit类中的static域被初始化。
  • 2、此时两个类的代码都已经加载完毕了。此时就来到了main方法里面,开始打印输出了『StaticInit Constructor』,然后就开始准备创建StaticInit的对象了。
  • 3、在创建对象之前,对象中的所有的基本数据类型都会被初始化为默认值,对象的引用会被初始化为null,然后开始调用基类的构造器,在开始构造基类对象的时候,会从上到下实例化类中的变量i,j(j会给与默认值0),然后进入基类的构造器。
  • 4、在基类构造器完成之后,就会回到子类StaticInit中的构造器,构造的顺序跟基类中的构造顺序是一样的。先从上到下实例化k,然后就会进入到构造器中,执行两个打印语句。此时就完成了StaticInit的对象的构造了,回到main里,结束运行。

以上代码的运行结果如下:

static Insect.x1 initialized
static StaticInit.x2 initialized
StaticInit Constructor
i=9 , j=0
StaticInit.k initialized
k=10
j=39

使用静态代码块

在一个类中,可以用static 关键字将一个段代码块进行包括,这被包括的代码就会像静态域一样进行加载,例如下面的一段代码,在定义book2和book4的时候并不是static的,但是,由于处于静态代码块中,所以book2和book4依然是用静态加载的方式进行的加载。

static {
        Book book2 = new Book("static成员book2成员变量初始化");
        Book book4 = new Book("static成员book4成员变量初始化");
    }

静态导包

就是在导入某一个包的时候,在import后面加上static的关键字,就将该包下的所有的类方法(即static方法)都直接导入,此时在访问所导入的类的类方法的时候,就不需要使用ClassName.methodName的方法了,而是直接使用methodName进行调用即可。

成员初始化

java 会尽量保证在每个变量在使用之前都是已经初始化了状态,对于方法的局部变量,如果在使用之前没有进行初始化,这会以编译器报错的形式来提醒。如果是类中的数据成员(即类的字段),是基本数据类型的时候,即时没有给出初始值,但是编译器会给它们赋予默认值。比如:int 的默认值是0,char的默认值是空的,float和double的默认值都是0.0。

this指针

this指针指的当前这个类的对象的引用,而static变量是不属于对象的,是属于类的,所以,this指针是不能访问静态的成员变量和方法的。

线程的创建和启动

线程的创建有两种方法:

  • 1、通过实现Runnable接口
  • 2、继承Thread类

在使用Runnable的时候,可以直接通过实现类的run方法运行,也可以将一个Runnable对象传递给Thread的构造器,然后调用Thread的对象的start()方法来启动这个线程。如果用Thread来启动线程的话,使用的是run方法,这此时的run方法则仅仅是一个普通方法。

继承和多态

class Base
{
    public void method(){} 
}
class Son extends Base
{
    public void method(){}

    public void methodB(){}
}

如上面,Son是Base的子类,在使用Base base = new Son();的时候,最后base是Base的实例对象,此时base是兺呢个访问到Son中的methodB方法。

replaceAll注意

replaceAll传入的两个参数中,第一个是一个正则表达式,在正则表达式中的”.”表示的是所有的字符,所以,形如下面的替换就会出现问题:

String str = "com.huaxin.";
str = str.replaceAll(".", "/");
System.out.println("str:"+str);

此时打印的是:

str:///////////

所以需要如下更正:

str = str.replaceAll("\\.", "/");

加\的原因是\本身是需要转义的。

接口和抽象类

抽象类

抽象方法:仅有声明,没有方法体的方法。如:

abstract void fo();

包含抽象方法的类称为抽象类。

如果继承一个抽象类,则该类必须为父抽象类的所有抽象方法提供方法的定义,否则,该类也必须是一个抽象类。

接口

是一个完全的抽象类,不会提供任何具体的实现(抽象类只是包含抽象方法,并不都是抽象方法,所以抽象类中是存在有方法体的方法的,而接口是完全不存在的),接口中也可以包含域,但是这些域是隐式的static和final的类型。如下的代码:

public class TestInterface {
    public static void main(String[] args) {
        System.out.println(Demo1.j);
    }
}
class Demo1 implements Demo3 {
    @Override
    public void foo() {
        System.out.println(i+"  "+j);
    }  
}
interface Demo3 {
    int i=0;
    int j=1; 
    public void foo();
}

另见:http://www.stevenwash.xin:8988

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章