向Java程序员的目标前进!
day17
面向对象—续7
学习内容
匿名内部类
顾名思义,匿名内部类就是没有名字的类,它是内部类的简化写法。
一般来说,开发很少使用具体类的匿名内部类,因为具体类本身就可以创建对象。
匿名内部类的前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类。
匿名内部类的格式
在一个类中的某个局部位置使用
new 类名(一般情况下:都是抽象类)或接口名() {
重写抽象类或者接口的抽象方法(){
...
}
};
匿名内部类的本质
是一个继承了该抽象类或者实现了接口的子类对象,本质还是多态
匿名内部类的应用场景
在抽象类和接口中使用最多,其中接口使用最多!
匿名内部类在开发中的使用
- 形参问题
- 如果方法形式参数是一个抽象类,调用方法的时候实际参数应该传递的当前抽象类的子类对象!
- 如果方法的形式参数如果是一个接口类型,调用方法的时候,需要传递接口的子实现类对象
- 返回值问题
- 方法的返回值如果是一个接口,需要接口的子实现类对象
JDK8新特性——拉姆达表达式
当接口中有且仅有一个抽象方法的时候,那么这个接口可以称为"函数式接口"。
函数式接口在实现方法的时候,有一个”->“箭头直接可以实现接口的方法
下面以例子分别展示接口的匿名内部类写法和拉姆达表达式写法
接口的匿名内部类写法
ld.show(new Love() {
@Override
public void love() {
System.out.println("爱学cs的小陈");
}
});
拉姆达表达式写法
ld.show( //接口的匿名内部类很多
()->{
System.out.println("爱学cs的小陈");
}
);
面试题:匿名内部类的使用
/**
* 看程序,补全代码,是程序在控制台输出"hello world"
* 考查匿名内部类的使用
*/
interface Inter{
void show() ;
}
class Outer{
//此处补全代码
}
public class Test {
public static void main(String[] args) {
Outer.method().show();
}
}
解析:
首先分析代码第14行,既然能用Outer直接调用method(),说明method是静态方法。method()后还可以调用show(),说明method()不仅存在返回值类型,返回的还是一个接口类型Inter。我们需要返回一个接口,那么就需要返回的接口的子实现类对象。但是程序现在没有子实现类名,那么最后就直接使用接口的匿名内部类。
答案:
public static Inter method(){
return new Inter(){
@Override
public void show() {
System.out.println("hello world");
}
};
}
Object类的几个方法—续
Object的clone方法
protected native Object clone() throws CloneNotSupportedException
:创建并返回此对象的副本。
clone的复制取决于对象的类,但是一般情况下是浅克隆,我们讨论的也仅仅是浅克隆。
clone方法是受保护的,而且是本地方法。如果不能实现Cloneable接口,则会抛出克隆不支持的异常(CloneNotSupportedException)。通常情况下,对于任意对象
x,判断的表达式是:
x.clone().equals(x)
这个结果将会是true。通过内存图来解释clone(浅克隆)的原理
上面的图解说明了:
- 产生的克隆对象只在栈内存中开辟区域
- 克隆对象与原对象的空间地址值是相同的
常用类
Scanner类
public boolean hasNextXXX(){} :判断下一个录入的数据是否为XXX类型
注:XXX就是对应的数据类型
public XXX nextXXX():以XXX形式扫描输入的下一个标记。
如果定义接收的数据类型和实际数据类型不匹配(实际的输入String,而接收的int),那么系统就会报java.util.InputMismatchException
的错误。
键盘录入的细节
问题:分别定义两个数据并录入,先录入String再录入int,没问题。先录入int再录入String,有问题。
原因:在录入int数据的时候,按了回车键。下一个录入的是String类型数据,录入时系统将换行当作空字符进去了,所以第二个数据没有录入程序就结束了。
解决方案:
- 使用
public String next()
方法,录入指定的字符串内容 - 仍然使用nextLine方法录入String。但是在录入String之前,重新创建一个新的键盘录入对象。
总结与建议:今后如果有int类型的数据需要录入,都可以拿String的nextLine方法接收(前提是录入格式要对)
String类
String类就是字符串类,数据值是常量,一旦被创建,其值就不能被更改。字符串类的底层是字符数组。
创建格式(建议的格式):
String 变量名 = "数据值";
String对象的数据值在创建时会存在与方法区的字符串常量池中。在赋值时会先在常量池中查找是否存在相同的字符串常量。如果存在就会指向常量池的对应常量地址值,否则会在常量池中开辟空间。
String类的特点:
- 字符串不变:字符串的值在创建后不能被更改。
- 共享性:因为String对象不可变,所以具有共享性。
- 重写了toString:String类型已经重写了Object的toString()功能。所以直接输出对象名,结果是对应的数据值。
String的构造方法
String()
:空参构造
当以空参构造创建String对象并直接输出时,将返回""(空的字符串)
String(byte[] bytes)
:有参构造,里面是字节数组(将byte[]转换为String)。作用是用平台默认字符集(现在是"UTF-8")解码字符数组,并构造一个新的字符串。
String(byte[] bytes, int offset, int length)
:将字节数组的指定部分解码并组成字符串。
byte[] bytes:字节数组对象
int offset:开始的位置
int length:指定的长度
String(char[] value)
:使字符数组组成新的字符串
String(char[] value, int offset, int count)
:使部分字符数组组成新的字符串
char[] value:字符数组对象
int length:指定的长度
String(String original)
:以括号里的字符串创建新的字符串对象(就是复制),参数为字符串常量值。
面试题:length()方法
Q:数组中有没有length(),String类中有没有length(),集合中有没有length()方法?
A:数组中没有length方法,有一个叫做length的属性。
String类中有length方法,是用来获取字符串长度的
集合中没有length方法,但是有size方法,是用来获取集合的元素数量
面试题:创建String对象的方式区别
Q:实际开发中,将常量赋值给字符串、new String(“常量”)、给String对象赋值为null和给和String对象赋值为""有什么区别?
A:四种方式是有区别的。
String对象赋值为null,则String对象为空对象,此时如果String对象的长度,则会报空指针异常。
String对象赋值为“”,则String对象为空字符序列,此时String对象的长度为0,可以用concat函数进行字符串的拼接。
.concat(String str); 拼接功能函数
注意:空对象和空字符串不是一个意思!
“”:空字符序列(空字符串),依然可以使用String类型里面的所有功能
null:空对象,不能在使用String的方法,一旦使用系统就会报空指针异常NullPointerException
采用将常量赋值给字符串的方式,是一种推荐的方式。采用此种方式后,JVM将直接在常量中找是否存在常量。如果存在,则返回该常量的内存地址值并返回,否则JVM将在常量池中开辟空间、创建对象。
如果在代码中直接new了String常量,这会使JVM在内存中创建两个对象(栈内存中对象、堆内存中对象)。JVM将按照创建对象的方式进行指针的引用。在堆内存中,创建的对象还会有常量标记,指向常量池的变量。所以这是一种不推荐的方式。
String常用的判断功能
1. public boolean contains(String s):字符串中是否包含指定的字符(连续的字符序列)(重点)
2. public boolean endsWith(String suffix):是否以指定的字符串结尾
3. public boolean startsWith(String prefix):是否以指定的字符串开头
4. public boolean isEmpty():判断字符串内容是否空字符,如果长度为0,结果为true
5. public boolean equals(Object anObject):重写了Object类的equals方法,以区分大小写的方式比较内容是否相同(重点)
6. public boolean equalsIgnoreCase(Object anObject):以不区分大小写的方式比较内容是否相同
String的equals方法的源码分析
/**
* String的equals方法源码
*/
public final class String{
//定义字符数组
private final char value[];
public boolean equals(Object anObject) {
// 进行内存地址值的比较,如果相同则返回true
if (this == anObject) {
return true;
}
// 判断对象是否为String类型的实例
if (anObject instanceof String) {
// 如果是,向下转型为String
String anotherString = (String)anObject;
// 取字符串的长度,相当于this.length
int n = value.length;
// 比较字符串和传入的字符串长度
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 将两个字符串进行逐个比较
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
}
String的获取功能
public char charAt(int index):获取指定索引处(索引就是位置的意思,从0开始)的字符值
public String concat(String str):拼接获取新的字符串,另外一种方式的拼接方法是"+"
public int indexOf(int ch):返回指定字符第一次出现的字符串内的索引值
public int indexOf(String str):返回指定字符串中子字符串第一次出现的索引值
public int lastIndexOf(int ch):返回指定字符最后一次出现的索引值
public int lastIndexOf(String str):同上
String substring(int beginIndex):从指定位置开始默认截取到末尾,返回一个新的字符串
String substring(int beginIndex, int endIndex):从指定位置开始截取到指定位置结束(从beginIndex到endIndex-1处都会被取到),返回的一个字符串
String的拆分功能
public String[] split(String regex):按照指定的分割符号,将字符串拆分成字符串数组
博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。