修饰符

Java修饰符主要分为俩类:

  • 访问修饰符:private、default、protected、public
  • 非访问修饰符:static、final、abstract、synchronized

访问修饰符权限

修饰符 当前类 同一包类 子类 其他包
public Y Y Y Y
protected Y Y Y N
default Y Y N N
private Y N N N

private

被private修饰的变量、方法仅限在本类中使用

所以private是最严的的访问级别,主要用于隐藏类的一些细节实现和保护类的数据。
例如pojo类就是使用private修饰变量,对外提供setter和getter的方法。
还有如果使用private用来修饰构造方法的话,该类是不能实例化的。这种在单例模式中可以经常看到!

虽然private主要用于修饰变量和方法,不过也可以修饰内部类,只不过是内部类。

public class Test{
    //修饰一个私有变量
    private int count=1;
    //修饰一个私有方法
    private int add(int i,int j){
        return i+j;
   }
    private class Test1{            
    }
}

private不能修饰外部类,因为Test类中的变量和方法是私有的,所以其他类无法调用!

public class Test2 {
    public static void main(String[] args) {
        Test t=new Test();
        //下面的变量和方法是无法获取的
        //t.count=2;
        //t.add(1,2);
    }
}

default

不使用任何修饰符。类、接口、变量、方法都可以使用。不过仅限在同一包下:

class Test{
     int count=1;
     int add(int i,int j){
            return i+j;
     }
    interface Test1{
    }
}

protected

被protected修饰的变量、方法仅仅对同一包内的类和所有子类可见。

public class Test{
    protected int count=1;
    protected int add(int i,int j){
            return i+j;
     }
     protected class Test1{
     }
}

在同包下可以直接调用,如果不在同包,则需要继承才可以使用:

public class Test2 extends Test{
    public static void main(String[] args) {
        Test t=new Test();
        t.count=2;
        t.add(1,2);
    }
}

protected不能修饰外部类

public

public:修饰的类、接口、变量、方法对所有类都可以使用。

public class Test{
  public int count=1;
  public int add(int i,int j){
          return i+j;
   }
}

非访问修饰符

其下有static,final,abstract,synchronized,transient,native

static

静态变量
static在修饰类变量的时候,无论该类被实例化了多少次,它的静态变量只有一份拷贝。静态变量也被称为类变量。局部变量是不能被声明为static变量的。

静态方法
static在修饰类方法的时候,静态方法是不能使用类的非静态变量。静态方法可以直接通过类名调用,因此静态方法中是不能用thissuper关键字的。

public class Test{
   public  String name="xuwujing";
   public  static String name2="xuwujing";

   public  static String getName() {
   //这个一句 会报错  因为静态方法是不能使用类的非静态变量
   //String reult=name;
   //这一句就可以
   String reult=name2;
   return reult;
    }

   //main方法是静态方法,可以直接调用本类中的静态方法和静态变量
   public static void main(String[] args) {
    System.out.println(name2);
    System.out.println(getName());
   }

   //该方法是不静态方法,所以调用本类中的静态方法和静态变量时,
   //需要使用classname.variablename和 classname.methodname的方式访问
   private void print(){
    System.out.println(Test.name2);
    System.out.println(Test.getName());
    }
   }

static 静态块:
在JVM类加载机制中,如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;如果类中存在初始化语句,就依次执行这些初始化语句。
可能上述的两句话不太好理解,那么这里我们来运行下代码查看其结果,通过结果可能就能更好的理解上述语句的话了。

class HelloA {
    public HelloA() {
        System.out.println("HelloA");
    }

    {
        System.out.println("I'm A class");
    }

    static {
        System.out.println("static A");
    }
}

public class HelloB extends HelloA {
    public HelloB() {
        System.out.println("HelloB");
    }

    {
        System.out.println("I'm B class");
    }

    static {
        System.out.println("static B");
    }

    public static void main(String[] args) {
        new HelloB();
    }
}

结果:
static A
static B
I'm A class
HelloA
I'm B class
HelloB

创建对象时构造器的调用顺序是:
先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。

那么static修饰符这块的运用可以总结如下:

  1. 静态变量在内存中只有一个拷贝,在类的所有实例中共享。
  2. 在静态方法中不能直接访问实例方法和实例变量,反之可以。
  3. 在静态方法中不能使用this和super关键字。
  4. 静态方法不能被abstract修饰。
  5. 静态方法和静态变量都可以通过类名直接访问。
  6. 当类被加载时,静态代码块只被加载一次。有多个静态变量或块时,按声明顺序加载。

final

final :用来修饰类、方法和变量。
final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
如果上述语句不好理解的话,我们可以通过编写相关代码进行实验。
定义一个final修饰的变量、方法以及类。然后进行相关的测试

public class Test{
       //定义一个final修饰的变量
   public  static final String name="xuwujing";

 public static void main(String[] args) {
         //这句会报错  因为该变量已经被final修饰了
         name="张三";
     }
     //类加上final之后,该类是无法被继承的
     final class Test2{
     }
     //这句会报错,因为Test2是被final修饰的类
     class Test3 extends Test2{
     }

     class Test4{
         //定义一个被final修饰的方法
          final Date getTime(){
             return new Date();
         }
     }

     class Test5 extends Test4{
         //这句会报错,因为final方法是不能被子类修改的。
         Date getTime(){
    return new Date();
         }
     }
 }

故得知:

  • final修饰类:表示该类不能被继承;
  • final修饰方法:表示方法不能被重写;
  • final修饰变量:表示变量只能一次赋值以后值不能被修改(常量);

abstract

abstract :用来创建抽象类和抽象方法。

修饰类:会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型(见后面实例),也就是编译时类型。抽象类就相当于一类的半成品,需要子类继承并覆盖其中的抽象方法。

修饰方法:会使这个方法变成抽象方法,也就是只有声明而没有实现,需要子类继承实现。

public class AbstractTest{
    public static void main(String[] args) {
        //这句会报错,因为抽象类不能实例化
        // Animal a=new Animal();
        //抽象类可以实例化重写该类抽象方法的子类
        Animal a = new Dog();
        a.show();
    }
}
abstract class Animal{
    abstract void show();
    public void print(){
        System.out.println("Animal");
    }
}
//继承抽象类需要实现抽象类的方法
class Dog extends Animal{    
    @Override
    void show() {
        System.out.println("This is Dog!");
    }
}

总结:
– 抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。
– 抽象类不可以创建实例,原因:调用抽象方法没有意义。
– 只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化。否则该子类还是一个抽象类。

注意:
– 抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
– 一个类不能同时被 abstract 和 final
修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
– 抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。 4、抽象方法不能被声明成 final 和 static。
– 任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
– 如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。

synchronized

synchronized: 修饰的方法同一时间只能被一个线程访问。在多线程中运用很常见。

synchronized 的解释:
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

简单的来说,就是使用synchronized 修饰的方法,在多线程进行同时访问的时候,只会让一个线程先进行访问,其它的线程等候,当这个线程访问完了之后,再让下一个进行访问,依次类推。

transient 和native

transient:被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
native: 被native修饰的方法实际是由另一种语言进行实现的本地方法。例如Java中获取的Long类型的时间戳 :System.currentTimeMillis(); 实际是由native 修饰的。

String

String 类型可能就是我们最常用的的对象了。
首先说明,String并不是基本数据类型,而是一个对象,并且是不可变的对象。查看源码可以String类是被final修饰的,是不可被继承的!

String的在未被初始化的时候为null,表示它还没有被创建,自然也就没有分配空间;
而” “和 new String()不是null,它们是已经被创建,只是值为空而已!并且也分配了内存空间。

String有15种构造方法,有两种是过时的,其中包含char[],byte[],int[],String,StringBuffer,StringBuilder
我们在创建String对象的的时候,一般是使用 String str=”xxx”,但有时也会用new String()来初始话字符串。
例如:

String hello="hello";
String newHello=new String("hello");
char []cHello ={'h','e','l','l','o'};
String str=new String(cHello);

注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。

String常用方法
大概讲述了String的用法之后,这里我们来列举一些String常用的方法:
– length :返回此字符串的长度。
– charAt:返回指定索引处的 char 值。
– compareTo:把这个字符串和另一个对象比较。
– concat:将指定字符串连接到此字符串的结尾。
– split:根据给定正则表达式的匹配拆分此字符串。
– equals:将此字符串与指定的对象比较。
– endsWith:测试此字符串是否以指定的后缀结束。
– startsWith:测试此字符串是否以指定的前缀结束。
– getBytes: 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
– indexOf:返回指定字符在此字符串中第一次出现处的索引。
– replace:返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 12:substring:返回一个新的字符串,它是此字符串的一个子字符串。
– ….

String对象比较

String作为我们最常用的对象,在面试中估计也会接触不少。一般来说,会考到String的常量池相关问题,主要是使用String进行比较的时候,==和equals这两种方法来判断是否相当。这里收集了一些String经常遇到的问题。

String s1 = "test";
String s2 = new String("test");
String s3 = "te";
String s4 = "st";
String s5 = "te" + "st";
String s6 = s3 + s4;
String s7 = new String(s1);
System.out.println(s1 == s2); 
System.out.println(s1 == s5); 
System.out.println(s1 == s6); 
System.out.println(s7==s1);       
System.out.println(s7.equals(s1));

結果:
false
true
false
false
true

解析:
– 虽然看起来是一样的,但是新建一个String类的时候会重新分配引用地址,而 == 就是比较引用地址,所以为false。
– 在编译之前就可以确认s5=test, 并且引用地址一样,所以为true;
– 字符串常量池的原则 这时 s6 的值是在运行时得到的,它会重新构造字符串对象 所以为false。
– 和第一个一样的,就是换汤不换药,所以为false。
– equals 只比较值相等,不关心它的引用地址。

String ab="ab";
String c="c";
String ab_c=ab+c;
String ab_c1="ab"+"c";
String abc="abc";
System.out.println(ab_c == abc + " : " + ab_c.equals(abc));
System.out.println((ab_c == abc) + " : " + ab_c.equals(abc));
System.out.println((ab_c1 == abc) + " : " + ab_c1.equals(abc));


结果:
false
false : true
true : true

解析:
到这里,可能就会诧异了,为什么和我想的不一样呢?
这里其实是有陷阱的,也就是运算符的优先级。
第一个结果就是优先级的问题导致的,它会先计算 abc + ” : ” + ab_c.equals(abc) ,然后再来进行比较,所以为false。同理,下面的也是如此,基本和上面的那个例子差不多,这里就不再概述了。

String、StringBuffer和StringBuilder

  • String: String的特点是一旦赋值,便不能更改其指向的字符对象,如果更改,则会指向一个新的字符对象。
  • StringBuffer:StringBuffer对象可以调用其方法动态的进行增加、插入、修改和删 除操作,且不用像数组那样事先指定大小,从而实现多次插入字 符,一次整体取出的效果,因而操作字符串非常灵活方便。并且生成数据之后可以toString转为String,线程安全。
  • StringBuilder:它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

关于字符串拼接方式,在String类中,我们最常用的是 + ,其次是使用StringBuffer或StringBuilder 的append方法,至于String类中的concat几乎很少用到。
一般来说,如果在少量的字符串进行拼接的话,我们会使用+,如果拼接过多的话,单线程使用 StringBuilder ,多线程使用StringBuffer 进行拼接。因为使用String 的 + 在过多的字符串进行拼接的时候会极大的使用内存,因为它在凭借的时候还是使用 append()方法,然后再进行toString转换,如果是少量的时候,是感觉不到差异的,但是在大量拼接的时候就会明显感受得到。

String str="Hello World";
String str1="";
StringBuffer sbr=new StringBuffer(str); 
StringBuilder sbd=new StringBuilder(str); 
long start=System.currentTimeMillis();
   for(int i=0;i<10000;i++){
       str1+=str;
   }
   System.out.println("String累加用时:"+(System.currentTimeMillis()-start)+"ms");
   long start2=System.currentTimeMillis();
   for(int i=0;i<10000;i++){
       sbr.append(str);
   }
   System.out.println("StringBuffer累加用时:"+(System.currentTimeMillis()-start2)+"ms");
   long start3=System.currentTimeMillis();
   for(int i=0;i<10000;i++){
       sbd.append(str);
   }
   System.out.println("StringBuilder累加用时:"+(System.currentTimeMillis()-start3)+"ms");


结果:
String累加用时:701ms
StringBuffer累加用时:2ms
StringBuilder累加用时:0ms

这里从输出结果中可以看到String 的+拼接方法的耗时了。但是使用 + 实在是方便。所以在这里建议如果字符串拼接次数在10次以下,可以使用+,过多的则用StringBuffer或StringBuilder。


淮城一只猫

永远年轻,永远热泪盈眶

发表评论

电子邮件地址不会被公开。 必填项已用*标注

我不是机器人*

EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00