查看: 85|回复: 0

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

[复制链接]
发表于 2020-2-16 07:54:19 | 显示全部楼层 |阅读模式
JavaSE学习条记(5)---内部类和String类

一.内部类基础

转自菜鸟教程
在 Java 中,可以将一个类定义在另一个类内里或者一个方法内里,这样的类称为内部类。广泛意义上的内部类一样平常来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来相识一下这四种内部类的用法。
1.成员内部类

成员内部类是最平凡的内部类,它的定义为位于另一个类的内部,形如下面的形式:
  1. 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成员和静态成员)。
  1. 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);   //外部类的静态成员        }    }}
复制代码
不外要留意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐蔽现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
  1. 外部类.this.成员变量外部类.this.成员方法
复制代码
固然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么为所欲为了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
  1. 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成员        }    }}
复制代码
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一样平常方式如下:
  1. 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 和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
2.局部内部类

局部内部类是定义在一个方法或者一个作用域内里的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
  1. 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 修饰符的。
3.匿名内部类

匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加轻易维护。下面这段代码是一段 Android 事件监听代码:
  1. 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             }});
复制代码
这段代码为两个按钮设置监听器,这内里就使用了匿名内部类。这段代码中的:
  1. new OnClickListener() {    @Override    public void onClick(View v) {        // TODO Auto-generated method stub             }}
复制代码
就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。
  1. 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成员必须依附于具体的对象。
  1. 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类


  • String类又称作不可变字符序列。
  • String位于java.lang包中,Java步调默认导入java.lang包下的所有类。
  • Java字符串就是Unicode字符序列,例如字符串“Java”就是4个Unicode字符’J’、’a’、’v’、’a’构成的。
  • Java没有内置的字符串类型,而是在尺度Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。
  • Java允许使用符号"+"把两个字符串连接起来。
String类的实例
  1. String e = ""  ; // 空字符串String greeting = " Hello World ";
复制代码
字符串连接
  1. String s1 = "Hello";String s2 = "World! ";String s = s1 + s2; //HelloWorld!
复制代码
n-符号"+"把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。
n-当"+"运算符两侧的操作数中只要有一个是字符串(String)类型,体系会主动将另一个操作数转换为字符串然后再进行连接。
"+"连接符
  1. 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都有一个运行时常量池,类在解析之后,将符号引用更换成直接引用,与全局常量池中的引用值保持同等。
常量池示例
  1. String str1 = "abc";String str2 = new String("def");String str3 = "abc";String str4 = str2.intern();String str5 = "def";System.out.println(str1 == str3);// trueSystem.out.println(str2 == str4);// falseSystem.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类的常用方法列表

String类常用方法一
  1. public class StringTest1 {    public static void main(String[] arg) {        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);    }}
复制代码
执行结果如图所示:

String类常用方法二
  1. 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不变    }}
复制代码
执行结果如图所示:

字符串相称的判断


  • equals方法用来检测两个字符串内容是否相称。如果字符串s和t内容相称,则s.equals(t)返回true,否则返回false。
  • 测试两个字符串除了大小写区别外是否是相称的,需要使用equalsIgnoreCase方法。
  • 判断字符串是否相称不要使用"=="。
忽略大小写的字符串比较
  1. "Hello".equalsIgnoreCase("hellO");//true
复制代码
字符串的比较"=="与equals()方法
  1. 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内里的字符串内容是一样的    }}
复制代码
执行结果如图所示:

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?用户注册

x

相关技术服务需求,请联系管理员和客服QQ:2753533861或QQ:619920289
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

帖子推荐:
客服咨询

QQ:2753533861

服务时间 9:00-22:00

快速回复 返回顶部 返回列表