Java笔记day17
一、内部类
1、定义
把类定义在其他类的内部,我们称之为内部类(相当于类的成员,不会生成class文件)
2、特点
1)、内部类可以访问外部类的成员,包括私有
2)、外部类要想访问内部类的成员,就必须要创建内部类的对象
查看代码
class Outer{
private int num = 10;
class Inner{
public void show(){
System.out.println(num); //可以直接访问外部类的成员,包括私有
}
}
public void show2(){
//show();
//创建内部类对象
Inner inner = new Inner(); //外部类要访问内部类需要先创建对象
inner.show();
}
}
public class InnerClassDemo1 {
public static void main(String[] args) {
}
}
3、分类
1)、根据内部类定义的位置不同,可以分为一下两种类型
成员的位置上(成员内部类)
局部的位置上(局部内部类)
查看代码
class Outer2{
class Inner2{ //定义在成员的位置上(成员内部类)
}
public void fun(){
class Inner3{ //定义在局部范围内(局部内部类)
}
}
}
public class InnerClassDemo2 {
}
2)、成员内部类及其特点
A、成员内部类调用格式
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
查看代码
class Outer3{
private int num = 10;
class Inner{
public void show(){
System.out.println(num);
}
}
}
public class InnerClassDemo3 {
public static void main(String[] args) {
//需求:我现在想要访问Inner类中的show()方法,就必须得创建对象
//Inner inner = new Inner(); 创建错误,成员内部类不能直接创建
//inner.show();
//正确创建内部类的格式
//外部类名.内部类名 对象名 = 外部类对象.内部类对象;
Outer3.Inner oi = new Outer3().new Inner();
oi.show();
//下面的方法和上面的方法都可以调用内部类,但是下面的方法更繁琐
//Outer3 outer3 = new Outer3();
//Outer3.Inner inner = new Outer3().new Inner();
//inner.show();
}
}
B、成员内部类常见的修饰符
成员内部类常见的修饰符为:
private:为了保证数据安全性
static:为了方便访问数据
静态修饰的特点:
内部类如果是静态修饰的,只能访问外部类静态的成员
静态成员内部类访问格式如下:
外部类类名.内部类类名 变量名 = new 外部类类名.内部类类名();
查看代码
class Outer4{
private int num = 10;
private static int num2 = 30;
static class Inner4{
public void show(){
//System.out.println(num); //报错,此处只能访问外部类当中的静态变量
System.out.println(num2);
}
public static void show2(){
//System.out.println(num);
System.out.println(num2); //这里也只能访问num2
}
}
}
public class InnerClassDemo4 {
public static void main(String[] args) {
//Error:(33, 29) java: 限定的新静态类,所以不能直接用上面的格式访问
//Outer4.Inner4 oi4 = new Outer4().new Inner4();
//oi4.show();
//当内部类是被静态所修饰的时候,出现了另一个创建内部类的格式
//访问格式如下:
//外部类类名.内部类类名 变量名 = new 外部类类名.内部类类名();
Outer4.Inner4 oi4 = new Outer4.Inner4();
oi4.show();
//oi4.show2(); 这样调用也是可以的,但是建议下面的调用,不需要创建对象
Outer4.Inner4.show2();
}
}
C、练习
补全代码,使其输出为30,20,10
查看代码
class Outer5 {
public int num = 10;
class Inner5 {
public int num = 20;
public void show() {
int num = 30;
System.out.println();
System.out.println();
System.out.println();
}
}
}
public class InnerClassDemo5 {
public static void main(String[] args) {
Outer5.Inner5 oi5 = new Outer5().new Inner5();
oi5.show();
}
}
/*解答****************************************************/
System.out.println(num); 30
System.out.println(this.num); 20
//Inner5与Outer5不是继承关系,没有super关键字
//System.out.println(super.num);
System.out.println(Outer5.this.num);(这种更好) 10
System.out.println(new Outer5().num); 10
3)、局部内部类及其特点
A、可以直接访问外部类的所有成员
B、可以在外部类中的局部范围中创建对象,通过对象调用
内部类中的方法,来使用内部类的局部功能。
查看代码
class Outer6{
private int num = 10;
public void fun(){
int num2 = 100;
class Inner6{
int num3 = 200;
public void show(){
//num3 = 300;
//从内部类引用的本地变量必须是最终变量或实际上的最终变量
//num2 = 400;
//通过反编译工具查看后发现局部内部类存在的方法中定义的局部变量自动加上了final
//相当于final int num2 = 100;
//特点:jdk1.8之后会自动添加final关键字。
System.out.println(num);
System.out.println(num2);
System.out.println(num3);
}
}
//创建内部类对象
Inner6 inner6 = new Inner6();
inner6.show();
public void fun2(){
//方法与方法之间内部访问不到局部内部类
//Inner6 inner6 = new Inner6();
}
}
public class InnerClassDemo6 {
public static void main(String[] args) {
Outer6 outer6 = new Outer6();
outer6.fun();
}
}
4、匿名内部类
1)、匿名内部类:
就是内部类的一个简化写法。
存在匿名内部类的前提:
要存在一个类或者是一个接口,类可以是具体的类也可以是抽象类
定义格式:
new 类名或者接口名(){重写方法;};
本质上:
是一个继承了这个类或者实现了这个接口的子类匿名对象
查看代码
interface A{
b();
c();
}
class B implements A{
b(){..}
c(){..}
}
A a = new B();
A a = new A(){
b(){..}
c(){..}
};
查看代码
interface Inter{
public abstract void show();
public abstract void show2();
}
class Outer7{
public void fun(){
new Inter(){
@Override
public void show() {
System.out.println("这是show方法");
}
@Override
public void show2() {
System.out.println("这是show2方法");
}
}.show();
new Inter(){
@Override
public void show() {
System.out.println("这是第二个匿名内部类的show方法");
}
@Override
public void show2() {
System.out.println("这是第二个匿名内部类的show2方法");
}
}.show2();
}
}
public class InnerClassDemo7 {
public static void main(String[] args) {
Outer7 outer7 = new Outer7();
outer7.fun();
}
}
**********对于Outer7,还有以下写法***********
class Outer7{
public void fun(){
//如果接口中的方法很多的时候,每次调用一个方法,都需要new一下,要写的内容都重复,此时怎么改进?
//多态的形式,这里叫做接口多态
Inter i = {
@Override
public void show() {
System.out.println("这是第二个匿名内部类的show方法");
}
@Override
public void show2() {
System.out.println("这是第二个匿名内部类的show2方法");
}
};
i.show();
i.show2();
}
}
2)、匿名内部类举例
第一种方式:不使用匿名内部类
查看代码
interface Person{
public abstract void study();
}
class PersonDemo{
//当接口作为方法的参数的时候,实际上需要的是实现该接口类的对象
public void fun(Person person){
person.study();
}
}
class Student implements Person{
@Override
public void study() {
System.out.println("好好学习天天向上");
}
}
public class InnerClassDemo8 {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
Person student = new Student();
pd.fun(student);
//pd.fun(new Student());匿名对象写法
}
}
第二种方式:使用匿名内部类
查看代码
interface Person{
public abstract void study();
}
class PersonDemo{
public void fun(Person person){
person.study();
}
}
public class InnerClassDemo8 {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
pd.fun(new Person() {
@Override
public void study() {
System.out.println("好好学习匿名内部类");
}
});
}
}
第三种方式:匿名内部类创建对象
查看代码
interface Person{
public abstract void study();
}
class PersonDemo{
Person person;
PersonDemo(Person person){
this.person = person;
}
}
public class InnerClassDemo8 {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo(new Person() {
@Override
public void study() {
System.out.println("好好学习使用匿名内部类创建对象");
}
});
Person person = pd.person;
person.study();
//或者可以写成下面格式
//new PersonDemo(new Person() {
// @Override
// public void study() {
// System.out.println("好好学习使用匿名内部类创建对象");
// }
//}).person.study();
}
}
3)、匿名内部类题目
补齐代码,要求在控制台输出Hello World
查看代码
interface Inter2 {
void show();
}
class Outer8 {
}
public class InnerClassDemo9 {
public static void main(String[] args) {
Outer8.method().show();
}
}
*******************************************
interface Inter2 {
void show();
}
class Outer8 {
//1、Outer8可以直接调用method方法,推出第一个结论:method方法是静态的
//2、第二个结论:由于main方法中调用完method方法之后还能继续调用方法
//所以得出method方法是有返回值的,由于show方法恰好是接口Inter2中的方法,所以返回值类型是接口Inter2类型
public static Inter2 method(){
//使用匿名内部类
return new Inter2() {
@Override
public void show() {
System.out.println("Hello World");
}
};
}
}
public class InnerClassDemo9 {
public static void main(String[] args) {
Outer8.method().show();
}
}
二、常用类
1、object类
1)、Object:Class Object是类Object结构的根,每个类都有Object作为超类。
所有对象(包括数组)都实现了这个类的方法,每个类都直接或者间接的继承Object类
2)、Object类中的方法:
A、hashCode()
public int hashCode()返回对象的哈希码值。
支持这种方法是为了散列表,如HashMap提供的那样
注意:这里的哈希码值是根据哈希算法计算出来的一个值,这个值和地址有关系,但是并不是实际的地址值。
B、getClass()
public final 类 getClass()返回此Object的运行时类。
返回的类对象是被表示类的static synchronized方法锁定的对象。
C、getName()
返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),返回值String类型
hashCode(),getClass(),getName()举例
查看代码
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.hashCode()); //1163157884
Student s1 = new Student();
System.out.println(s1.hashCode()); //1956725890
Student s2 = s;
System.out.println(s2.hashCode()); //1163157884
System.out.println("*************************");
Student s3 = new Student();
System.out.println(s3.getClass()); //class com.shujia.yhl.day17.Student
Class c = s3.getClass();
System.out.println(c.getName()); //com.shujia.yhl.day17.Student
System.out.println("****************************");
//链式编程
System.out.println(s3.getClass().getName());
}
D、toString()
public String toString()返回对象的字符串表示形式。
一般来说, toString方法返回一个“textually代表”这个对象的字符串。结果应该是一个简明扼要的表达,
容易让人阅读。 建议所有子类覆盖此方法。
该toString类方法Object返回一个由其中的对象是一个实例,该符号字符`的类的名称的字符串@ ”和对象
的哈希码的无符号的十六进制表示。换句话说,这个方法返回一个等于下列值的字符串:
getClass().getName() + '@' + Integer.toHexString(hashCode())
Integer类中有一个toHexString方法将哈希值转换成地址值
public static String toHexString(int i),返回整数参数的字符串表示形式,作为16位中的无符号整数。
我们虽然掌握了toString()的方法使用,但是呢打印的结果是一个地址值,我们拿到地址值是没有意义的
我们正常打印一个对象,其实是想看该对象中的成员变量的值。又因为toString()方法的返回值是String类型,并且属于Object类中的
而Object类是所有类的父类,那么我们自己定义类的时候,就可以重写该方法,重写里面的实现。将来调用toString()方法的时候,
严格遵循编译看左,运行看右,所以调用的是重写后的方法。
ALT+INS,然后就可以找到需要重写的tiString()方法
类最终写法:4.0版本:
成员变量:private
构造方法:无参,有参
成员方法:getXxx()和setXxx(),..
查看代码
toString():自动生成即可
public class StudentTest2 {
public static void main(String[] args) {
Student3 s = new Student3();
System.out.println(s.toString());//com.shujia.wyh.day18.Student3@4554617c
System.out.println("*************************");
System.out.println(s.getClass().getName());
System.out.println("*****************************");
//toString()方法等价于以下内容
//getClass().getName() + '@' + Integer.toHexString(hashCode())
//s.getClass().getName() + "@" + Integer.toHexString(s.hashCode())
//this.getClass().getName() + "@" + Integer.toHexString(this.hashCode())
System.out.println(s.toString());
System.out.println(s.getClass().getName()+"@"+Integer.toHexString(s.hashCode()));
s.setName("小花");
s.setAge(18);
System.out.println(s.toString());
}
}
E、equals()
public boolean equals(Object obj)指示一些其他对象是否等于此。
今后想要弄明白一个类中的方法是如何实现的时候,API也没告诉我们怎么办呢?
看源码。将鼠标光标放在方法名上 ctrl+鼠标左键
通过观察源码发现:Object类中的equals方法实现底层依旧是==
public boolean equals(Object obj) {
return (this == obj);
}
而==比较引用数据类型比较的是地址值,当地址值不一样的时候返回的是false
==:
基本类型:比较的是值是否相同
引用类型:比较的地址值是否相同
equals:
引用数据类型:
不需要我们自己重写,自动生成即可,ALT+INS,然后找到equals(),自动重写
重写之前,默认调用的是Object类中的equals方法,默认比较的是地址值
重写之后,比较的是对象成员变量值
查看代码
public class StudentTest3 {
public static void main(String[] args) {
Student4 s1 = new Student4("阿伟", 18);
Student4 s2 = new Student4("阿伟", 18);
System.out.println(s1==s2);
Student4 s3 = s1;
System.out.println(s1==s3);
System.out.println("***************************");
System.out.println(s1.equals(s2)); //false(没有重写时)true(重写之后)
System.out.println(s1.equals(s3)); //true
System.out.println(s1.equals(s1)); //true
//但是,我们两个对象的成员变量值都一样,从现实生活角度出发,我们应该创建的是同一个学生
Student4 s4 = new Student4("段坤", 18);
System.out.println(s3.equals(s4)); //false
}
}
F、clone()和finalize()
protected Object clone()
创建并返回此对象的副本。
protected void finalize()
throws Throwable当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。
一个子类覆盖了处理系统资源或执行其他清理的finalize方法。
这个方法简单理解为就是用来垃圾回收的,什么时候回收呢?不确定。
GC机制
clone的方法Object执行特定的克隆操作。
首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException,
然后将想要调用克隆方法的类实现Cloneable接口,实现接口之后就可以调用了
通过观察API发现,Cloneable接口中没有成员变量也没有成员方法
今后看到类似于Cloneable接口里面什么都没有的接口,我们称之为标记接口,只做标记
拷贝与直接赋值的区别
Object obj = s1.clone();
Student5 s2 = s1;
拷贝之后再修改值,原本s1的值不会改变,而s2由于是直接给的地址值,所以两个都会发生改变
拷贝在IT行业中常见两种:
浅拷贝
拷贝的时候,重新创建一个对象,成员变量值和被拷贝的一样,但是后续修改与原先的没有关系
深拷贝
拷贝的时候,没有创建新的对象,只是改个名字,地址值都一样,修改拷贝后的连带影响到原先的
查看代码
public class StudentTest4 {
public static void main(String[] args) throws CloneNotSupportedException {
Student5 s1 = new Student5("韩琛", 18);
System.out.println(s1.toString());
//CloneNotSupportedException
//这里其实隐含了多态
Object obj = s1.clone();
System.out.println(obj.toString());
Student5 s2 = s1;
System.out.println(s2.toString());
System.out.println("**************************");
s2.setAge(20);
//((Student5)obj)
System.out.println(s1.toString());
System.out.println(s2.toString());
System.out.println(obj.toString());
}
}