JavaSE学习笔记(5)---内部类和String类

JavaSE学习笔记(5)---内部类和String类

一.内部类基础

转自菜鸟教程html

​ 在 Java 中,能够将一个类定义在另外一个类里面或者一个方法里面,这样的类称为内部类。普遍意义上的内部类通常来讲包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。java

1.成员内部类

​ 成员内部类是最普通的内部类,它的定义为位于另外一个类的内部,形以下面的形式:app

class Circle {
    double radius = 0;
     
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println("drawshape");
        }
    }
}

​ 这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类能够无条件访问外部类的全部成员属性和成员方法(包括private成员和静态成员)。ide

class Circle {
    private double radius = 0;
    public static int count =1;
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println(radius);  //外部类的private成员
            System.out.println(count);   //外部类的静态成员
        }
    }
}

​ 不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认状况下访问的是成员内部类的成员。若是要访问外部类的同名成员,须要如下面的形式进行访问:函数

外部类.this.成员变量
外部类.this.成员方法

​ 虽然成员内部类能够无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么为所欲为了。在外部类中若是要访问成员内部类的成员,必须先建立一个成员内部类的对象,再经过指向这个对象的引用来访问:学习

class Circle {
    private double radius = 0;
 
    public Circle(double radius) {
        this.radius = radius;
        getDrawInstance().drawSahpe();   //必须先建立成员内部类的对象,再进行访问
    }
     
    private Draw getDrawInstance() {
        return new Draw();
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println(radius);  //外部类的private成员
        }
    }
}

​ 成员内部类是依附外部类而存在的,也就是说,若是要建立成员内部类的对象,前提是必须存在一个外部类的对象。建立成员内部类对象的通常方式以下:测试

public class Test {
    public static void main(String[] args)  {
        //第一种方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();  //必须经过Outter对象来建立
         
        //第二种方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 
class Outter {
    private Inner inner = null;
    public Outter() {         
    }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {
        public Inner() {             
        }
    }
}

​ 内部类能够拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限。好比上面的例子,若是成员内部类 Inner 用 private 修饰,则只能在外部类的内部访问,若是用 public 修饰,则任何地方都能访问;若是用 protected 修饰,则只能在同一个包下或者继承外部类的状况下访问;若是是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不同,外部类只能被 public 和包访问两种权限修饰。我我的是这么理解的,因为成员内部类看起来像是外部类的一个成员,因此能够像类的成员同样拥有多种权限修饰。this

2.局部内部类

​ 局部内部类是定义在一个方法或者一个做用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该做用域内。code

class People{
    public People() {
         
    }
}
 
class Man{
    public Man(){
         
    }
     
    public People getWoman(){
        class Woman extends People{   //局部内部类
            int age =0;
        }
        return new Woman();
    }
}

 

注意: 局部内部类就像是方法里面的一个局部变量同样,是不能有 public、protected、private 以及 static 修饰符的。htm

3.匿名内部类

匿名内部类应该是平时咱们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,并且使代码更加容易维护。下面这段代码是一段 Android 事件监听代码:

scan_bt.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub         
    }
});
 
history_bt.setOnClickListener(new OnClickListener() {
     
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
});

这段代码为两个按钮设置监听器,这里面就使用了匿名内部类。这段代码中的:

new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
}

就是匿名内部类的使用。代码中须要给按钮设置监听器对象,使用匿名内部类可以在实现父类或者接口中的方法状况下同时产生一个相应的对象,可是前提是这个父类或者接口必须先存在才能这样使用。固然像下面这种写法也是能够的,跟上面使用匿名内部类达到效果相同。

private void setListener()
{
    scan_bt.setOnClickListener(new Listener1());       
    history_bt.setOnClickListener(new Listener2());
}
 
class Listener1 implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub             
    }
}
 
class Listener2 implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub             
    }
}

​ 这种写法虽然能达到同样的效果,可是既冗长又难以维护,因此通常使用匿名内部类的方法来编写事件监听代码。一样的,匿名内部类也是不能有访问修饰符和 static 修饰符的。

​ 匿名内部类是惟一一种没有构造器的类。正由于其没有构造器,因此匿名内部类的使用范围很是有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为 Outter$1.class。通常来讲,匿名内部类用于继承其余类或是实现接口,并不须要增长额外的方法,只是对继承方法的实现或是重写。

4.静态内部类

​ 静态内部类也是定义在另外一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不须要依赖于外部类的,这点和类的静态成员属性有点相似,而且它不能使用外部类的非static成员变量或者方法,这点很好理解,由于在没有外部类的对象的状况下,能够建立静态内部类的对象,若是容许访问外部类的非static成员就会产生矛盾,由于外部类的非static成员必须依附于具体的对象。

public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}
 
class Outter {
    public Outter() {      
    }
     
    static class Inner {
        public Inner() {    
        }
    }
}

String类

  1. String类又称做不可变字符序列。

  2. String位于java.lang包中,Java程序默认导入java.lang包下的全部类。

  3. Java字符串就是Unicode字符序列,例如字符串“Java”就是4个Unicode字符’J’、’a’、’v’、’a’组成的。

  4. Java没有内置的字符串类型,而是在标准Java类库中提供了一个预约义的类String,每一个用双引号括起来的字符串都是String类的一个实例。
  5. Java容许使用符号"+"把两个字符串链接起来。

String类的实例

String e = ""  ; // 空字符串
String greeting = " Hello World ";

字符串链接

String s1 = "Hello";
String s2 = "World! ";
String s = s1 + s2; //HelloWorld!

​ n-符号"+"把两个字符串按给定的顺序链接在一块儿,而且是彻底按照给定的形式。

​ n-当"+"运算符两侧的操做数中只要有一个是字符串(String)类型,系统会自动将另外一个操做数转换为字符串而后再进行链接。

"+"链接符

int age = 18;
String str = "age is" + age;  //str赋值为"age is 18"
//这种特性一般被用在输出语句中:
System.out.println("age  is" + age);

常量池

在Java的内存分析中,咱们会常常听到关于“常量池”的描述,实际上常量池也分了如下三种:

1. 全局字符串常量池(String Pool)

全局字符串常量池中存放的内容是在类加载完成后存到String Pool中的,在每一个VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。

2. class文件常量池(Class Constant Pool)

class常量池是在编译的时候每一个class都有的,在编译阶段,存放的是常量(文本字符串、final常量等)和符号引用。

3. 运行时常量池(Runtime Constant Pool)

运行时常量池是在类加载完成以后,将每一个class常量池中的符号引用值转存到运行时常量池中,也就是说,每一个class都有一个运行时常量池,类在解析以后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。

常量池示例

String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);// true
System.out.println(str2 == str4);// false
System.out.println(str4 == str5);// true

示例中首先通过编译以后,在该类的class常量池中存放一些符号引用,而后类加载以后,将class常量池中存放的符号引用转存到运行时常量池中,而后通过验证,准备阶段以后,在堆中生成驻留字符串的实例对象(也就是上例中str1所指向的“abc”实例对象),而后将这个对象的引用存到全局String Pool中,也就是String Pool中,最后在解析阶段,要把运行时常量池中的符号引用替换成直接引用,那么就直接查询String Pool,保证String Pool里的引用值与运行时常量池中的引用值一致,大概整个过程就是这样了。

回到示例的程序,如今就很容易解释整个程序的内存分配过程了,首先,在堆中会有一个“abc”实例,全局String Pool中存放着“abc”的一个引用值,而后在运行第二句的时候会生成两个实例,一个是“def”的实例对象,而且String Pool中存储一个“def”的引用值,还有一个是new出来的一个“def”的实例对象,与上面那个是不一样的实例,当在解析str3的时候查找String Pool,里面有“abc”的全局驻留字符串引用,因此str3的引用地址与以前的那个已存在的相同,str4是在运行的时候调用intern()函数,返回String Pool中“def”的引用值,若是没有就将str2的引用值添加进去,在这里,String Pool中已经有了“def”的引用值了,因此返回上面在new str2的时候添加到String Pool中的 “def”引用值,最后str5在解析的时候就也是指向存在于String Pool中的“def”的引用值,那么这样一分析以后,结果就容易理解了。

String类经常使用方法

转自尚学堂笔记

String类是咱们最常使用的类。字符串类的方法咱们必须很是熟悉!咱们列出经常使用的方法,请你们熟悉。

String类的经常使用方法列表

图2.png

String类经常使用方法一

public class StringTest1 {
    public static void main(String[] args) {
        String s1 = "core Java";
        String s2 = "Core Java";
        System.out.println(s1.charAt(3));//提取下标为3的字符
        System.out.println(s2.length());//字符串的长度
        System.out.println(s1.equals(s2));//比较两个字符串是否相等
        System.out.println(s1.equalsIgnoreCase(s2));//比较两个字符串(忽略大小写)
        System.out.println(s1.indexOf("Java"));//字符串s1中是否包含Java
        System.out.println(s1.indexOf("apple"));//字符串s1中是否包含apple
        String s = s1.replace(' ', '&');//将s1中的空格替换成&
        System.out.println("result is :" + s);
    }
}

执行结果如图所示:

图5-31 示例5-29运行效果图.png

String类经常使用方法二

public class StringTest2 {
    public static void main(String[] args) {
        String s = "";
        String s1 = "How are you?";
        System.out.println(s1.startsWith("How"));//是否以How开头
        System.out.println(s1.endsWith("you"));//是否以you结尾
        s = s1.substring(4);//提取子字符串:从下标为4的开始到字符串结尾为止
        System.out.println(s);
        s = s1.substring(4, 7);//提取子字符串:下标[4, 7) 不包括7
        System.out.println(s);
        s = s1.toLowerCase();//转小写
        System.out.println(s);
        s = s1.toUpperCase();//转大写
        System.out.println(s);
        String s2 = "  How old are you!! ";
        s = s2.trim();//去除字符串首尾的空格。注意:中间的空格不能去除
        System.out.println(s);
        System.out.println(s2);//由于String是不可变字符串,因此s2不变
    }
}

执行结果如图所示:

图5-32 示例5-30运行效果图.png

字符串相等的判断

  1. equals方法用来检测两个字符串内容是否相等。若是字符串s和t内容相等,则s.equals(t)返回true,不然返回false。

  2. 要测试两个字符串除了大小写区别外是不是相等的,须要使用equalsIgnoreCase方法。

  3. 判断字符串是否相等不要使用"=="。

忽略大小写的字符串比较

"Hello".equalsIgnoreCase("hellO");//true

字符串的比较"=="与equals()方法

public class TestStringEquals {
    public static void main(String[] args) {
        String g1 = "北京尚学堂";
        String g2 = "北京尚学堂";
        String g3 = new String("北京尚学堂");
        System.out.println(g1 == g2); // true  指向一样的字符串常量对象
        System.out.println(g1 == g3); // false  g3是新建立的对象
        System.out.println(g1.equals(g3)); // true  g1和g3里面的字符串内容是同样的
    }
}

执行结果如图所示:

图5-33 示例5-32运行效果图.png

示例的内存分析如图所示:

图5-34 示例5-32内存分析图.png

相关文章
相关标签/搜索