目录
一.String类和常量池
1.String基础知识
String数称作不可变字符序列。
String位于java.lang包中,Java 程序默认导入java.lang包下的所有类。
Java字符串就是Unicode字符序列,例如字符串"Java" 就是4个Unicode字符’J'、'a'、'v'、' a'组成的。
Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类:
String,每个用双引号括起来的字符串都是String 类的一个实例。
String类的函数对员原字符串都没有影响,都会在原字符串的基础上生成新字符串!
toString()方法是打印:类名@对象地址,但是打印字符串的时候字符串却是原样输出,因为String类重写了toString()方法。
示例代码(含有==,equals与去除空格的知识点):
public class string {
public static void main(String[] args) {
String str1="abc";
String str2=new String("abc");
System.out.println(str1==str2);
System.out.println(str1);//会自动调用toString()函数,但是String类重写了
System.out.println(str2);
System.out.println(str1.equals(str2));//比较字符串内容时用此函数
System.out.println();
String str3=" a b ";//长度7,前后俩空格,中间一个空格
String str4=str3.trim();//去除头尾两端的空格
System.out.println(str4.length());
System.out.println(str4);
//String是不可变字符序列,所有的替换,截取子字符串,去空格,转换大小等都是生成了新字符串
System.out.println(str3.replace(" ",""));//去除所有空格
System.out.println(str3);//原字符串不变
}
}
输出:
false
abc
abc
true
3
a b
ab
a b
str1==str2返回false的原因如下图:
可见str1的地址是0x1,而str2的地址是0x2,str1==str2比较的又是对象地址,显然返回false
2.常量池
在Java的内存分析中,我们会经常听到关于“常量池”的描述,实际上常量池也分了以下三种:
1.全局字符串常量池(String Pool)
全局字符串常量池中存放的内容是在类加载完成后存到String Pool中的,在每个VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。
2. class文件常量池(Class Constant Pool)
class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量(文本字符串、final 常量等)和符号引用。
3.运行时常量池(Runtime Constant Pool)
运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,
也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。
二.内部类
1.概念
内部类是一类特殊的类,指的是定义在一个类的内部的类。 实际开发中,为了方便的使用外部类的相关属性和方法。这时候我们通常会定义一个内部类。
内部类分为成员内部类,匿名内部类,局部内部类;其中成员内部类又分为非静态内部类和静态内部类。
一般情况 ,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内部定义,称为内部类(innerclasses)。
内部类可以使用public、default、 protected 、private 以及static修饰。而外部顶级类(我们以前接触的类)只能使用public和default修饰。
2.内部类的作用
内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
3.注意
内部类只是一个编译时概念, 一旦我们编译成功, 就会成为完全不同的两个类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在, 其成员变量/方法名可以和外部类的相同。
右键包名,选择show in Explorer就可查看编译之后的字节码文件,找一找可看到编译之后的内部类和外部类生成了如下两个文件。
4.非静态内部类
非静态内部类(外部类里使用非静态内部类和平时使用其他类没什么不同)
1.非静态内部类对象必须寄存在一 个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
2.非静态内部类可以直接访问外部类的成员 ,但是外部类不能直接访问非静态内部类成员。
3.非静态内部类不能有静态方法、静态属性和静态初始化块。
4.成员变量访问要点 :
1.内部类里方法的局部变量:变量名。
2.内部类属性 : this.变量名。
3.外部类属性 :外部类名.this变量名。
示例代码1:
//外部类
public class Outer1 {
private int age=10;
private void show(){
System.out.println("要你好看!");
}
//内部类
public class Inner1{
private String name="tom";
private int age=20;
public void showInner(){
System.out.println("Inner.showInner");
System.out.println(age);
//当外部类属性和内部类属性发生重名时,可以通过:Outer1.this.成员名 来调用
System.out.println(Outer1.this.age);
show();//内部类可以直接使用外部类的成员!
}
}
public static void main(String[] args) {
//创建内部类对象
Outer1.Inner1 inner01=new Outer1().new Inner1();
inner01.showInner();
//下面的定义方式和上面效果一样
Outer1 out02=new Outer1();
Inner1 inn02=out02.new Inner1();
inn02.showInner();
}
}
输出:
Inner.showInner
20
10
要你好看!
Inner.showInner
20
10
要你好看!
代码2:
public class Face {
private String shape="国字脸";
//内部类
class Nose{
private String shape="高鼻梁";
public void breath(){
System.out.println("鼻子长在了"+Face.this.shape+"形状的脸上");
System.out.println("呼吸,呼吸,呼吸");
}
}
class Ear{
}
class Eye{
}
public static void main(String[] args) {
Face.Nose nose=new Face().new Nose();
nose.breath();
}
}
输出:
鼻子长在了国字脸形状的脸上
呼吸,呼吸,呼吸
5.静态内部类
定义方式:
static class ClassName {
//类体
}
使用要点:
1.静态内部类可以访问外部类的静态成员,不能访问外部类的普通成员。
2.静态内部类看做外部类的一个静态成员。
代码:
/*
测试静态内部类
*/
public class testStaticInnerClass {
public static void main(String[] args) {
//通过new外部类名.内部类名来创建内部类对象
Outer2.Inner2 inner2=new Outer2.Inner2();
inner2.test();
}
}
class Outer2{
private int a=10;
private static int b=20;
//相当于外部类的一个静态成员
static class Inner2{
public void test(){
//System.out.println(a);//静态内部类不能访问外部类的普通属性
System.out.println(b);//静态内部类可以访问外部类的普通属性
}
}
}
输出:
20
6.匿名内部类
适合那种只需要使用一次的类。比如:键盘监听操作等等。在安卓开发、awt、 swing开发中常见。
语法:
new 父类构造器(实参类表) \实现接口() {
//匿名内部类类体!
}
代码:
//匿名内部类
public class TestAnonymousClass {
public void test(A a){
a.run();
}
public static void main(String[] args) {
TestAnonymousClass tac=new TestAnonymousClass();
tac.test(new AA());
tac.test(new A(){
@Override
public void run() {
System.out.println("TestAnonymousClass。run");
}
});
}
}
class AA implements A{
@Override
public void run() {
System.out.println("AA.run");
}
}
interface A{
void run();
}
输出:
AA.run
TestAnonymousClass.run
7.局部内部类
定义在方法内部的,作用域只限于本方法,称为局部内部类。
局部内部类的的使用主要是用来解决比较复杂的问题想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。
局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用, 出了该方法就会失效。
局部内部类在实际开发中应用很少。
代码:
/**
* 测试局部内部类
*/
public class TestLocalInnerClass {
public void show(){
//作用域仅限于该方法
class Inner3{
public void fun(){
System.out.println("hello world");
}
}
new Inner3().fun();
}
public static void main(String[] args) {
new TestLocalInnerClass().show();
}
}
输出:
hello world