内部类,Object,字符串
内部类,Object,字符串
内部类
内部类的存在允许了一个类定义在另一个类之中
内部类的基本语法
class 类名 {
// 内部类
class 类名 {
}
内部类的分类
成员内部类
顾名思义,成员内部类即将类当成外部类的一个成员变量
- 成员内部类可以直接调用外部类的成员变量(包括private)→实例化对象之后就无法访问了
- 由于外部类实例化的过程中已经携带了内部类的信息,即给类信息分配好了内存,则我们实例化内部类时只需要调用已经实例化完成了的外部类对象.
OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
通过类实例化OuterClass.InnerClass innerClass = outerClass.new InnerClass();
通过外部类对象实例化
public class OuterClass {
private int outVar=100;
public class InnerClass{
public void display(){
System.out.println("外部类变量为:"+outVar);//内部类可以直接访问外部类变量
}
}
}
public class Test {//测试类
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
//类实例化
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
//外部类对象实例化
}
}
静态内部类
静态内部类就是被static关键字修饰的内部方法,简单来说就是把他放入static池中,
- 不用实例化外部类对象就可以调用其中的静态成员方法和静态成员变量
- 静态内部类可以直接调用外部静态方法和静态变量
public class OuterClass {
private static int outVar=100;
public static class StaticInnerClass{ //静态内部类
public static void display(){
System.out.println("外部类变量为:"+outVar);//内部类可以直接访问外部类变量
}
public void notStaticDisplay(){ //非静态内部类
System.out.println("外部类变量为:"+outVar);
}
}
}
public class Test {
public static void main(String[] args) {
OuterClass.StaticInnerClass.display();//当调用静态方法时不需创建实例对象
OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();
//当调用非静态方法时需创建实例对象
staticInnerClass.notStaticDisplay();
}
}
局部内部类
局部内部类是定义再方法内部或任意代码块中的类
-
局部内部类的作用域:只能在其被定义的方法或代码块中使用,包括其构造函数
public class OuterClass { private int outVar=100; public void doSomething(){ class LocalInnerClass { void display() { System.out.println("外部类的成员变量值为:"+outVar);//可以访问外部类的成员 } } //可以在方法中实例化局部内部类 LocalInnerClass localInnerClass = new LocalInnerClass(); localInnerClass.display(); } //会报错 LocalInnerClass localInnerClass = new LocalInnerClass(); localInnerClass.display(); }
访问外部类的成员:局部内部类可以访问外部类的所有成员(包括private),但不能访问定义它们的方法(即它们所在的方法)和或代码块的参数(除非其是final
匿名内部类
匿名内部类是没有类名内部类,通常用于实现接口或者继承类,继承或者实现接口的时候,如果子类只用到了一次,则可以直接使用匿名内部类来是实现
接口 变量名=new 接口() //基本语法
{
};
public class OuterClass
{
int OutVar=100;
//已知外部已经存在一个接口OneInterface
OneInterface oneInterface=new OneInterface() {
@Override
public void display() {
System.out.println(OutVar);
}
};
} //内部类的基本用法
Object
在java中,Object类是所有类的根类.这意味着一个类没有明确继承的类时,则它默认继承Object类.
Object中几个重要的方法
equals()
- 用途:用于比较两个对象的相等性(而不是它们的引用→内存地址)
- 通常情况下,
equals()
是使用==
来比较对象的内存地址,即比较两个对象是否是一个对象.但实际情况中,我们通常要比较两个对象的内容是否一致,这是就得重写equals()
方法 equals()
与==
的区别- 对于基本数据类型:如
int,double,float,Boolean
,==
操作符的作用是比较两个值是否相等,即直接比较两个值在内存中的表示是否相同. - 对于引用类型:如 数组,接口,类等,
==
操作符是用于比较两个引用是否指向内存中同一个对象,简单来说就是比较它们的内存地址是否相同,而不是比较它们的内容是否相同 - 我会在讲解了
String
类之后给大家举个例子
- 对于基本数据类型:如
toString()
- 用途:返回该对象的字符串表示形式
- 默认情况下,
toString()
方法返回的字符串是类名+”@”+对象的哈希码和无符号十六进制表示形式.但实际开发我们通常需要更具体,更有意义的字符串表达形式,这是我们可以对toString()
方法进行重写
默认toString()方法的example:
public class Person
{ private int age;
String sex;
//getter setter 方法
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.toString());
}
}
输出为:
重写toString()
方法
public class Person {
private int age;
String sex;
String name;
//构造一个构造器便于我们理解toString()的重写
public Person(int age, String sex, String name) {
this.age = age;
this.sex = sex;
this.name = name;
}
//重写toString()
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex="
+sex+
'}';
}
public static void main(String[] args) {
Person person = new Person(18,"man","Jack");
System.out.println(person.toString());
}
}
输出为:
hashCode()
- 用途:返回该对象的哈希码值.(哈希码都是唯一的整数).哈希码值主要用于哈希数据结构如,
HashMap
,HashSet
- 默认情况下,
hashCode()
返回的哈希码是对象的内存地址,但如果重写了equals()
方法通常都要重写hashCode()
方法
hashCode()
返回的效果
finalize()
- 用途:在垃圾收集器决定回收某对象之前调用此方法
- 默认情况下,我们不用手动去调用,由JVM来调用,当垃圾回收器确定不存在该对象的引用时,由对象的垃圾回收器调用此方法,某些情况下,我们可能执行一些具体的功能如 关闭文件,断开数据库等等,这时需要我们重写
finalize()
方法
getClass()
- 用途:返回此
Object
运行时类的Class对象 - 该方法在反射和泛型类型检查等场景中特别有用,由于这节课不涉及该知识点,我们之后学习到再详细讲解
String(字符串类
String
是java中一个核心类,用于表示字符序列(即文本).
不可变性
- 核心特性:
String
对象的内容是不可变的,一旦一个String
对象被创建,它的内容就不能被改变 - 缓存特性:字符串池(String Pool) 可以储存已经创建过的字符串对象,避免重复创建相同的字符串对象
字符串创建的方法
1. 字面量创建
- 直接使用
=
与””
字符序列来创建字符串
String str1="Hello";
- 通过这种方法来创建的字符串,本质上是通过字符串池来引用已有的字符串常量;
2. 构造方法创建
- 通过
String
类的构造方法来创建字符串对象
String str1=new String("Hello");
- 通过这种方法来创建字符串,是再堆内存中申请了一个新的
String
对象,即创建了一个新的对象
字符串池(StringPool
- 概念: 字符串池是java堆内存中一个特殊区域,用于存放字符串的字面量(即代码中的字符串
””
中的) - 特点:当创建一个字符串字面量时,JVM首先会检查字符串池中是否已经存在相同的字符串.如果有则返回其引用(即指向已存在的字符串常量);否则,再字符串池中创建一个新的字符串对象,并返回其引用
该特性也可以用来区分equals()
与==
的区别
public class Test {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str1 == str2); //true
System.out.println(str1 == str3);//false
System.out.println(str3 == str4);//false
//equals()
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3));//true
System.out.println(str3.equals(str4));//true
}
}
分析:
首先程序开始,JVM先扫描静态区和字符串常量,先找到
”Hello”
,先放入字符串池中,接着String str1 = "Hello";String str2 = "Hello";
这两步的作用是将str1,str2的引用指向了字符串池中的”Hello”
常量;因此str1与str2两个指向的是同一个地址.
接着通过
String
构造函数创建了两个新对象,str3,str4
str3
与str4
实际保存的是新new出来的实例对象的值,而这两个实例对象中有一个value值,指向的是字符串池中的”Hello”
常量
如下图具体:
字符串常用的方法
方法 | 描述 |
---|---|
public boolean equals(Object anObject) | 比较字符串的内容,严格区分大小写。 |
public boolean equalsIignoreCase(String anotherString) | 比较字符串的内容,忽略大小写。 |
public int length() | 返回此字符串的长度。 |
public char charAt(int index) | 返回指定索引处的 char 值。 |
public char[] toCharArray() | 将字符串拆分为字符数组后返回。 |
public String substring(int beginIndex, int endIndex) | 根据开始和结束索引进行截取,得到新的字符串(包含头,不包含尾)。 |
public String substring(int beginIndex) | 从传入的索引处截取,截取到末尾,得到新的字符串。 |
public String replace(CharSequence target, CharSequence replacement) | 使用新值,将字符串中的旧值替换,得到新的字符串。 |
public String[] split(String regex) | 根据传入的规则切割字符串,得到字符串数组。 |
public byte[] getBytes() | 获得当前字符串底层的字节数组。 |
StringBuilder 和 StringBuffer(在线程中会更详细讲解)
- 由于
String
的不可变性,频繁的字符串连接操作会导致大量的中间字符串对象被创建和销毁,从而影响性能。为了解决这个问题,Java 提供了StringBuilder
和StringBuffer
类,它们允许在内存中直接修改字符序列,从而提高性能。 StringBuilder
不是线程安全的,而StringBuffer
是线程安全的(但性能略低于StringBuilder
)。在单线程环境中,通常使用StringBuilder
;在多线程环境中,如果需要线程安全,则使用StringBuffer
。