内部类
-
在类中再定义一个类。
演示的代码如下:class OuterClass{ // 外部类 // 属性 // 方法 // 构造方法 class InnerClass{ // 内部类 } }
内部类也会生成独立的.class文件命名方式为外部类$内部类.class。
1 成员内部类
1.1定义方式
-
定义位置:类以内方法以外。
演示的代码如下:class OuterClass{ // 外部类 class InnerClass{ // 成员内部类 } }
1.2 创建成员内部类对象
- 成员内部类对象必须依赖外部类对象,先创建外部类对象再创建成员内部类对象。
演示的代码如下:
package com.txw.test;
public class TestMemberClass {
public static void main(String[] args) {
OuterClass oc = new OuterClass(); // 创建外部类对象
OuterClass.InnerClass ic = oc.new InnerClass(); // 创建内部类对象
}
}
// 外部类
class OuterClass{
// 成员内部类
class InnerClass{
}
}
2 静态内部类
2.1 定义方式
- 定义位置:类以内方法以外。
演示的代码如下:class OuterClass{ // 外部类 static class InnerClass{ // 静态内部类 } }
2.2 创建静态内部类对象
- 创建静态内部类对象不依赖外部类对象。
演示的代码如下:
package com.txw.test;;
public class TestStaticClass {
public static void main(String[] args) {
// 创建静态内部类对象
OuterClass.InnerClass oi = new OuterClass.InnerClass();
}
}
class OuterClass{ // 外部类
static class InnerClass{ // 静态内部类
}
}
3 局部内部类
3.1 定义方式
-
定义位置:方法以内。
演示的代码如下:class OuterClass{ // 外部类 public void method(){ class InnerClass{ // 局部内部类 } } }
3.2 创建局部内部类对象
- 局部内部类对象只能在当前方法中使用。
演示的代码如下:
package com.txw.test;;
public class TestLocalClass {
public static void main(String[] args) {
}
}
class OuterClass{ // 外部类
public void method(){
class InnerClass{ // 局部内部类
}
InnerClass ic = new InnerClass();
}
}
4 匿名内部类
4.1 定义方式
- 定义位置:与局部一致,需要定义在方法中。
演示的代码如下:class Super{} class Sub extends Super{} class OuterClass{ public void method(){ // 匿名内部类必须以子类的形式出现 Super sup = new Super(){ // 匿名内部类,属于Super的子类 }; } }
4.2 创建匿名内部类对象
- 匿名内部类对象必须作为子类出现只有{}表示,创建对象与定义类同时执行。
演示的代码如下:
匿名内部类是学习Lamdba表达式与流式编程的重要前提。interface IA{ public void m1(); } class OuterClass{ public void method(){ IA a; a = new IA(){ // 匿名内部类 } } }
演示的代码如下:
package com.txw.test;
public class TestAnonymityClass {
public static void main(String[] args) {
new OuterClass().method();
}
}
class OuterClass{
public void method(){
MyInterface mi;
mi = new MyInterface(){
public void m1(int n){
System.out.println(n);
}
};
MyInterface mi2;
mi2 = System.err::print; // Lambda表达式
mi2.m1(1200);
// method2(mi);
}
public void method2(MyInterface mi){
}
}
interface MyInterface {
public void m1(int n);
}
API(Application Programming Interface)
- 用户编程接口。
Object类(对象)
- Java是一门纯面向对象的语言。
万物皆对象,万物 is a Object
- 特点:
1. Object类是所有类的父类,所有类默认继承Object 2. Object类型的引用可以存储任何对象,Object作为参数可以接收任何类型的数据,作为返回值可以返回任何类型的数据 3. Object是所有类的父类,Object中定义的方法是所有类都具备的方法
常用方法
1 getClass()
- 作用:返回引用中存储的对象类型,通常用于类型判断。
演示的代码如下:
演示的代码如下:class Animal{} class Dog extends Animal{} // 判断b引用中存储的对象是否为Animal类型 // 只获取Animal类型的对象 System.out.println(b instanceof Animal); // true instanceof具有多态性 // 使用getClass()进行判断 System.out.println( a.getClass() ); System.out.println( b.getClass() ); // 类型一致返回true System.out.println(a.getClass() == b.getClass()); // false 两种类型不一致
package com.txw.test;
public class TestObject {
public static void main(String[] args) {
Animal a = new Animal();
Animal b = new Dog();
// 判断b引用中存储的对象是否为Animal类型
// 只获取Animal类型的对象
System.out.println(b instanceof Animal);
// 使用getClass()进行判断
System.out.println( a.getClass() );
System.out.println( b.getClass() );
// 类型一致返回true
System.out.println(a.getClass() == b.getClass());
}
}
// class Worker extends Object{}
// class Worker{}
// class Student{}
class Animal{}
class Dog extends Animal{}
2 toString()
- 作用:将对象转换为一个可读性较强的字符串并返回Object类中的默认实现:类名+@+十六进制的hash编码。
缺点:可读性差,无法表达对象的特点。 - 当父类的方法不再适用于子类时,覆盖toString方法。
演示的代码如下:
注意:当使用System.out.println打印语句输出对象时,会自动调用对象中的toString方法打印该方法的返回值。class Student{ String name; int age; double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override // 可以检查注解下的方法是否满足覆盖的语法要求 public String toString(){ return "Student[ name = "+name+" , age = "+age+" , score ="+score+" ] "; } }
演示的代码如下:
package com.txw.test;;
public class TestToString {
public static void main(String[] args) {
Student stu1 = new Student("阿森",18,3D);
Student stu2 = new Student("wangyx",16,100D);
System.out.println( stu1.toString() );
// 如果直接打印对象 自动调用对象中的toString方法打印返回值
System.out.println( stu2 );
}
}
class Student{
String name;
int age;
double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override // 可以检查注解下的方法是否满足覆盖的语法要求
public String toString(){
return "Student[ name = "+name+" , age = "+age+" , score ="+score+" ] ";
}
}
写好一个类时添加toString方法是一种编程习惯。
3 equals()
- 作用:判断两个对象中的内容是否相同。
使用 == 判断只能判断对象的地址是否相同,而地址不同属性相同也应该视为相同的对象。
所以在引用类型的判断中使用 == 是不准确的。
演示的代码如下:// 内容(属性的值)一样则会视为相同的对象 Student stu1 = new Student("阿森",18,3D); Student stu2 = new Student("阿森",18,3D); System.out.println( stu1 == stu2 ); // 判断引用类型中的地址是否一致
- Object中的equals方法默认实现与 == 判断一致,依然是根据地址判断两个对象是否相同。
public boolean equals(Object obj) { return (this == obj); }
- 父类的equals不再适用于子类,无法对属性进行判断无法区分对象是否重复。
覆盖父类的equals方法。
演示的代码如下:
equals方法作为验证对象是否相同的重要方法,为类添加equals方法是一种编程习惯。class Student{ String name; int age; double score; @Override public boolean equals(Object obj) { // 如果地址相同,对象内容一定相同 if(this==obj)return true; // 如果传入的obj为null直接返回false因为没有可比较的对象 if(obj==null) return false; // 验证两个对象的类型是否一致 if(getClass() != obj.getClass() ) return false; // 验证两个对象的属性值是否一致 Student other = (Student)obj; // 向下转型 if(age == other.age && score == other.score && name.equals(other.name) ) return true; return false; }
演示的代码如下:
package com.txw.test;;
import java.util.Scanner;
public class TestEquals {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String input = sc.next();
// String str = "ABC";
// System.out.println("input = " + input);
System.out.println( input == "ABC" );
System.out.println( input.equals("ABC") );
// 内容(属性的值)一样则会视为相同的对象
Student stu1 = new Student("阿森",18,3D);
Student stu2 = new Student("阿森",18,3D);
Student stu3 = stu2;
/*Teacher t1 = null;
System.out.println( stu1 == stu2 ); // 判断引用类型中的地址是否一致
System.out.println( stu1.equals(stu2) );
System.out.println( stu1.equals(t1) );
*/
System.out.println(stu2);
System.out.println(stu3);
System.out.println( stu2 == stu3 );
}
}
class Teacher{}
class Student{
String name;
int age;
double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
// stu1.equals( stu2 );
// this = stu1
// obj = stu2
// this == obj stu1 == stu2
/* @Override
public boolean equals(Object obj) {
// 如果地址相同,对象内容一定相同
if(this==obj)return true;
// 如果传入的obj为null直接返回false因为没有可比较的对象
if(obj==null) return false;
// 验证两个对象的类型是否一致
if(getClass() != obj.getClass() ) return false;
// 验证两个对象的属性值是否一致
Student other = (Student)obj; // 向下转型
if(age == other.age && score == other.score && name.equals(other.name) )
return true;
return false;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Double.compare(student.score, score) == 0 && name.equals(student.name);
}
/* @Override // 可以检查注解下的方法是否满足覆盖的语法要求
public String toString(){
return "Student[ name = "+name+" , age = "+age+" , score ="+score+" ] ";
}*/
/* @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}*/
}
4 finalize()
- 作用:JVM垃圾回收器(GC)回收对象释放内存前调用finalze()方法。
Java官方并不推荐程序员覆盖此方法以改变原有的垃圾回收策略。
垃圾对象:零引用对象
演示的代码如下:
JVM会适时回收没有引用指向的对象。Student stu = new Student(); stu = null;
习题
- (内部类)Java 中内部类包括(ABCD)
A. 成员内部类。
B. 静态内部类。
C. 局部内部类。
D. 匿名内部类。
E. 公开内部类 。
F. 抽象内部类 。 - (内部类)关于 Java 中的内部说法正确的是(ABC)。
A. 成员内部类中可以访问外部类的所有属性或是方法 。
B. 静态内部类中可以访问外部类的所有的属性或是方法 。
C. 局部内部类只能在当前类中创建对象 。
D. 局部内部类可以访问所在方法中的常亮 。
原因:局部内部类创建对象只能在当前方法中。 - (内部类)仔细阅读以下代码,下面哪些选项放在 //1 处可以编译通过(ABD)
A. System.out.println(value1);
B. System.out.println(value2);
C. System.out.println(value3);
D. System.out.println(value4);
原因:
I. 局部内部类可以访问外部类的属性(A)以及静态属性(B)。
II. 局部内部类可以访问局部变量,但是要求局部变量必须是 final 的。
III. C 和 D 选项都是局部变量,但是 C 错误,因为 value3 没有被声明为final的。
4. (内部类)编程:已知 Light 接口如下,根据下面要求完成代码。
把 TestLamp 类补充完整,要求:
(1) 在//1 处使用局部内部类技术,调用 lamp 的 on 方法要求输出”shine in red”
(2) 在//2 处使用匿名内部类技术,调用 lamp 的 on 方法要求输出”shine in yellow”。
答:
代码如下:
package com.txw.test;
interface Light{
void shine();
}
class Lamp{
public void on (Light light){
light.shine();
}
}
class RedLight implements Light{
@Override
public void shine() {
System.out.println("“shine in red");
}
}
class YellowLight implements Light{
@Override
public void shine() {
System.out.println("shine in yellow");
}
}
public class TestLamp{
public static void main(String[] args) {
Lamp lamp = new Lamp();
lamp.on(new RedLight());
lamp.on(new YellowLight());
}
}
- (Object 类)仔细阅读以下代码,写出程序运行的结果。
答:输出结果:null 0 Tom 18
打印一个对象,相当于打印该对象的字符串表现形式,即:toString 方 法返回值。 Student 类覆盖了toString 方法,返回 Student 类的name和age属性。由于stu1对象的name属性和age属性没有被初始化, 因此其值为默认值。 - (Object 类)仔细阅读以下代码,写出程序运行的结果;并简述 == 和 equals 的区别。
答: 输出结果:true、false
第一个比较使用 equals,比较的是对象的值。
第二个比较使用==,比较的是对象的地址。 - (Object 类)仔细阅读以下代码,写出程序运行的结果。
答:输出结果:true、false
第一个比较,判断的是 a1 引用所指向的对象,是否是 Animal 类型或 者其子类。由于 a1 指向的对象是Dog 类 型,是 Animal 类型的子类, 因此比较结果为 true 。
第二个比较,比较的是 a1 所指向的对象的实 际类型,和 a2 所指向对象的实际类型是否相同。由于 a1 所指向的对 象为 Dog 类型,而 a2 所指向的对象为 Animal 类型,因此这个比较 返回值为 false。 - (Object 类)仔细阅读以下代码,//1 处填入哪些代码可以编译通过(ABC)。
A. stu1 + “ ” + 100
B. 100 + “ ” + stu1
C. “ ” + 100 + stu1
D. stu1 + 100 + “ ”
原因:
I. 当一个对象与一个字符串相加时,会自动调用对象的 toString 方法, 从而把该对象转化为字符串,再进 行字符串的连接。
II. A/B/C 三个选项,最终都会成为“字符串+对象”的形式,因此编译 都能通过。
III. D 选项错误,因为 stu+100 做的是对象和一个整数的加法,这种 运算没有定义,因此编译不通过。
9. (Object 类)在 Object 类中,定义的 finalize 方法在被垃圾回收时调用;toString 方法返回值表示对象的字符串 表现形式; equals 方法的作用 为判断两个对象值是否相同; getClass 方法作用为获得对象的实际类型。
10. (Object 类)编程:定义一个用户类(User 类),属性有用户名、用户密码 password、电话(tel), 要求如下:
(1) 对类进行封装,提供 get/set 方法;同时提供无参数、有参数的构造方法。
(2) 覆盖 toString 方法,要求格式为: username:一如既往,password:123456,tel:13051800681。
(3) 覆盖 equals 方法,要求:只要用户名相同则为相同对象 (4) 写一个测试类,利用键盘分别输入两个用户信息并存储 Use 对象中,判断两个对象是否相同。
演示的代码如下:
package com.txw.test;
import java.util.Scanner;
public class TestLamp{
public static void main(String[] args) {
User[] users = new User[2]; int index = 0;
Scanner sc = new Scanner(System.in);
for (int i = 1; i <= 2; i++) {
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码: ");
String password = sc.next();
System.out.println("请输入手机号:");
String tel = sc.next();
User u = new User(username, password, tel);
users[index] = u;
index++;
}
System.out.println(users[0].equals(users[1])); }
}
// 用户类
class User {
// 属性私有提供set/get方法
private String username;
private String password;
private String tel;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public User() {
}
public User(String username, String password, String tel) {
this.username = username;
this.password = password;
this.tel = tel;
}
//覆盖toString()
@Override
public String toString() {
return "username: " + username + ", password:" + password + ", tel:" + tel;
}
// 覆盖equals(),要求只要用户名相同则表示同一个对象
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
//只判断有户名
if (this.username.equals(other.username)) {
return true;
} else {
return false;
}
}
}
- (包装类)以下包装类的类名正确的是(ABDEFH) 。
A. Byte
B. Short
C. Int
D. Long
E. Float
F. Double
G. Char
H. Boolean - (包装类)下面关于数据类型之间的转换正确的是(ABD) 。
A. Integer i = 5 ;
B. int a = new Integer(12);
C. Integer i2 = 20 + “”;
D. String s = true+””;
原因:Intege - int之间能够自动拆箱/装箱。
C错误,因为20+“ ”最终结果是个字符串,字符串无法自动转成Integer。 - (包装类)仔细阅读以下代码,写出程序运行打印输出的结果;并解释每个结果的原因。
答: true false false
第一个为true是因为两个Intger对象都自动拆箱为int类型,int类型用 == 比较,比较的是具体的数值,都是128。 剩下两个都为false,是因为每个对象都是通过new关键字创建的,== 比较地址所以为false。 - (String 类)仔细阅读以下代码段: String s = “hello”; String t=“hello”; char[] c = {‘h’,‘e’,‘l’,‘l’,‘o’}; 下列选项输出结果为 false 的语句是(B)。
A. System.out.println( s.euqals( t ) );
B. System.out.println( t.equals( c ) );
C. System.out.println( s==t );
D. System.out.println( t.equals( new String© ) );
E. System.out.println( t.equals( new String(“hello”)) ); - (String 类)关于 java.lang.String 类,以下描述正确的一项是(A)。
A. String 类是 final 类故不可以继承。
B. String 类是 final 类故可以继承。
C. String 类不是 final 类故不可以继承。
D. String 类不是 final 类故可以继承。 - (String 类)应用程序的 main 方法中有以下语句,则输出的结果是(A)。
A. false
B. true
C. 1
D. 0 - (String 类)已知 String 对象 s=”abcdefg”,则 s.substring(2,5)的返回值为( B)。
A.”bcde”
B.”cde”
C.”cdef”
D.”def”
原因:截取方法包括开始的数值,不包括结束的数值。 - (String 类)已知 s 为一个 String 对象,s=”abcdefg”,则 s.charAt(1)的返回值为(B)。
A.a
B.b
C.f
D.g
原因:根据下标获取字符。 - (String 类)仔细阅读以下代码,关于程序描述正确的是(B)。
A、编译错误,String 类没有 replace 方法。
B、运行后输出的结果是 abcde
C、运行后输出的结果是 abxde
D、编译成功,运行时抛出异常,提示 String 类的内容是不可变的。
原因:使用替换方法,替换后得到的字符串并没有接收,而是打印原有字符串, 所以内容不变。 - (String 类)仔细阅读以下代码,执行后的结果是(C )。
A. aceg
B. abc
C. bdf
D. abcdefg
原因:打印字符循环下标从1开始,每次i+2,打印的下标依次是1,3,5。 - (String 类)请解释 Sting 和 StingBuilder 操作字符串时的区别。
答: String一旦创建内容和长度不可变。
StringBuilder内容和长度可变。 - (String 类)编程:任意输入一个字符串,统计字符串中字母的个数。
演示的代码如下:
package com.txw.test;
public class Test{
public static void main(String[] args) {
String s = "123baizhi,.BAIZHI";
int count = 0;
// 统计字符串中字母的个数
for (int i = 0; i < s.length(); i++) {
// 循环得到字符串中每一个字符
char ch = s.charAt(i);
// 如果当前字符在a~z或者A~Z之间统计变量++
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
count++;
}
}
// 循环结束字母个数统计完毕
System.out.println(count);
}
}
- (String 类)编程:给定一个邮箱“zhangmj@zparkhr.com”,根据下面要求完成程序代码:
(1) 获取邮箱中的用户名:zhangmj。
(2) 验证邮箱“zhangmj@zparkhr.com”是否是一个合法的邮箱格式。 提示:① 邮箱必须包含“@”和“ . ” ② 最后一个“ . ”的位置必须大于“@”的位置。
演示的代码如下:
package com.txw.test;
public class Test{
public static void main(String[] args) {
String email = "zhengcg@zparkhr.com";
String userName = email.substring(0, email.indexOf('@'));
if(email.indexOf('@') != -1 && email.lastIndexOf('.') != -1){
if(email.indexOf('@') < email.lastIndexOf('.')){
System.out.println("邮箱合法");
}
}
}
}
- (String 类)编程:编写一个方法,功能是实现判断某字符串数组中是否有重复元素。 请自行拟定合适的方法名称,实现方法自行选择,可实现要求即可; 例如:public boolean 方法名(String[] strArray){…}。
演示的代码如下:
package com.txw.test;
public class Test {
public static void main(String[] args) {
String[] str = { "hello", "hello", "world", "java", "android" };
System.out.println(check(str));
}
// 判断字符串中是否包含重复元素 、
public static boolean check(String[] strArray) {
/*定义一个标记值,如果有重复flag设置为true。
使用选择排序的思想,拿固定值和之后的每一个值作比较equals,
如果遇到内容相同的flag设置为true。
最终判断flag的值,false表示无重复,true表示有重复。
*/
boolean flag = false;
m:for (int i = 0; i < strArray.length; i++) {
for (int j = i+1; j < strArray.length; j++) {
if (strArray[i].equals(strArray[j])) {
flag = true;
break m;
}
}
}
return flag;
}
}
- (String 类)编程:在给定的字符串“ABCDEFGhijklmn1234567”中获取随机的 4 个字符,并使用 StringBuilder 拼接成字符串。(随机获取到的 4 个字符中可以出现重复字符) 提示:创建随机数对象 java.util.Random。 java.util.Random random = new java.util.Random(); random.nextInt(100); //可以获取到 0~99 中任意一个随机数 。
演示的代码如下:
package com.txw.test;
import java.util.Random;
public class Test {
public static void main(String[] args) {
String str = "ABCDEFGhijklmn1234567";
Random random = new java.util.Random();
StringBuilder sb = new StringBuilder();
//随机获取字符串中四个字符,使用StringBuilder拼接
for (int i = 0; i < 4; i++) {
int index = random.nextInt(str.length());
char ch = str.charAt(index);
sb.append(ch);
}
System.out.println(sb);
}
}
- (String 类)编程:将给定的字符串进行反转,并打印输出反转之后的字符串。 例如:给定的字符串为 “hello” 反转的字符串为 “olleh” 。
演示的代码如下:
package com.txw.test;
public class Test {
public static void main(String[] args) {
// 字符串反转打印反转之后的内容
String s = "hello";
StringBuilder sb = new StringBuilder(s);
System.out.println(sb.reverse());
}
}
- (String 类)编程:给定一个由数字组成的字符串,如:“1239586838923173478943890234092”;统 计出每个数字出现的次数。
演示的代码如下:
package com.txw.test;
public class Test {
public static void main(String[] args) {
// 统计字符串中每个数字出现的次数
// 方式1:
String str = "1239586838923173478943890234092";
// 数组长度为10,第一个空间记录的是0字符出现的次数,以此类推
int[] c = new int[10];
// 循环得到字符串中每一个字符
for(int i = 0 ; i < str.length(); i++){
char ch = str.charAt(i);
// 0字符对应的int值是48,1对应49以此类推 。
// 当前字符-48的值可以作为下标对应空间进行++。
int a = ch-48;
c[a]++;
}
for(int i : c){
System.out.println(i);
}
// 方式2:
// 循环,i从48开始到56结束,使用i变量表示字符0~9对应的整数。
for(int i = 48; i <=56; i++){
System.out.println(i - 48 + "出现:");
// 第一次内层循环判断字符0有几个,第二次判断字符1有几个。
int count = 0;
for(int j = 0; j<str.length() ;j++){
if(i == str.charAt(j) ){
count++;
}
}
System.out.println(count+"次");
}
}
}
总结