一、内部类
1、把类定义在其他类的内部,我们称之为内部类
内部类的特点:
1、内部类可以访问外部类的成员,包括私有
2、外部类要想访问内部类的成员,必须要创建内部类的对象
class Outer{
private int num = 10;
class Inner{
public void show(){
System.out.println(num);
}
}
public void fun(){
// show();
//创建内部类
Inner inner = new Inner();
inner.show();
}
}
public class InnerClassDemo1{
public static void main(String[] args){
Outer outer = new Outer();
// outer.show();
outer.fun();
}
}
2、按照内部类在类中定义的位置不同,可以分为如下两种方式:
成员位置(成员内部类)
局部位置(局部内部类)
class Outer2{
//定义在成员的位置上(成员内部类)
class Inner2{
}
public void fun(){
//定义在局部范围内(局部内部类)
class Inner{
}
}
}
public class InnerClassDemo2{
}
---------------------------------------------------------------------------------------------
成员内部类:
1、定义在类的成员位置上
2、内部类可以访问外部类的成员,包括私有的
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 inner3 = new Inner();
// 正确创建成员内部类对象的格式
//外部类名.成员内部类名 对象名 = new 外部类名().new 成员内部类名();
// Outer.Inner oi3 = new Outer3().new Inner(0;
// oi3.show();
Outer3 outer3 = new Outer3();
Outer3.Inner inner = outer3.new Inner();
inner.show();
}
}
-----------------------------------------------------------------------------------------------------------
成员内部类常见的修饰符:
private:其他类不能直接创建内部类的对象,要想使用被private修饰内部类成员,必须在本类中间接的创建对象调用
static:内部类如果被static修饰的时候,只能访问外部类中静态成员
class Outer4{
// private int num = 200;
private static int num = 200;
// private class Inner4{
// public void fun(){
// System.out.println(num);
// }
//}
static class Inner4{
public static void fun(){
System.out.println(num);
}
}
// public void show(){
// Inner4 inner4 = new Inner4();
// inner.fun();
// }
public class InnerClassDemo4{
public static void main(String[] args){
// Outer4.outer4 oi4 = new Outer().new Inner4();
// oi4.fun();
// Outer4 outer4 = new Outer4();
// outer.show();
// Outer4.Inner4 oi4 = new Outer4.new Inner4();
//当内部类是被静态所修饰的时候,出现了另外一种创建内部类的方式
//格式如下:
// 外部类名.内部类名 对象名 = new Outer4.inner4();
// Outer4.Inner4 oi4 = new Outer4.Inner4();
// oi4.fun();
// 直接通过类名直接调用
Outer.Inner.fun();
}
}
----------------------------------------------------------------------------------------------------------------------
Test:
class Outer {
public int num = 10;
class Inner{
public int num = 20;
public void show(){
int num = 30;
System.out.println();
System.out.println();
System.out.println();
}
}
}
在控制分别输出:30,20,10
class Outer5 {
public int num = 10;
class Innner5 {
public int num = 20;
public void show(){
int num = 30;
System.out.println(num);//30
System.out.println(this.num);//20
//Outer5和Inner5不是继承关系,没有super关键字
// System.out.println(super.num);//10
// System.out.println(new Outer5().num);
System.out.println(Outer5.this.num);
}
}
}
public calss InnerClassDemo5 {
public static void main(String[] args){
Outer5.Inner5 oi5 = new Outer5(0.new Inner5();
oi5.show();
}
}
-------------------------------------------------------------------------------------------------------------------------------
局部内部类:
1、定义在方法中的类
2、局部内部类可以直接访问外部类中所有成员
3、要想使用局部内部类中的方法,在定义局部内部类的方法中,创建局部内部类对象调用方法
class Outer6{
private int num = 10;
public void fun(){
int num2 = 20;
class Inner6{
int num3 = 300;
public void show(){
num3 = 30;
//在局部内部类中引用的本地变量必须是最终变量或者实际上的最终变量
//通过反编译工具观察发现,存在局部内部类的方法中定义的局部变量自动加上了final关键字
// 在JDK1.8之后会自动加上final关键字
// num2 = 22;
num = 44;
System.out.println(num);
System.out.println(num2);
System.out.println(num3);
}
}
Inner6 inner6 = new Inner6();
inner6.show();
}
}
public class InnerClassDemo6 {
public static void main(String[] args){
Outer6 outer6 = new Outer6();
outer6.fun(0;
}
}
二、匿名内部类
interface A{
b();
c();
}
class B implements A{
b(){....};
c(){....};
}
A a = new B();
a.b();
a.c();
匿名内部类:
语句定义格式:
new 类名 (可以是抽象类也可以是具体的类)/接口(){
要重写的方法;
};
上面的案例通过匿名内部类改进:
A a = new a(){
b(){...}
c(){...}
};
匿名内部类存在的前提:
要存在一个类或者一个接口,这个类可以是具体的类也可以是抽象类
interface Innter{
public abstract void show();
public abstract void show2();
}
// class B implements Inner{
// @Override
// public void show(){
// }
// @Override
// public void show2(){
// }
//}
//Inter i = new B();
calss Outer7{
public void fun(){
//使用匿名内部类的形式创建对象调用show方法
new Inter(){
@Override
public void show(){
System.out.println("这是show方法");
}
@Override
public void show2(){
System.out.println("这是show2方法");
}
}.show();
//使用匿名内部类的形式创建对象调用show2方法
new Inter(){
@Override
public void show(){
System.out.println("这是show方法");
}
@Override
public void show2(){
System.out.println("这是show2方法");
}
}.show2();
//想一想,我现在的接口中,只有两个方法,方法的个数比较少,每调用一个方法,都要new一下,并且new之后的内容都是一样的
// 如果以后接口中的方法很多的时候,你再用这样的方法就很麻烦
//怎么样改进呢?
//利用接口多态的形式给匿名内部类起名字
Inter ineter = new inter(){
@Override
public void show(){
System.out.println("这是show方法");
}
@Override
public void show2(){
System.out.println("这是show2方法");
}
};
inter.show();
inter.show2();
}
}
public class InnerClassDemo7 {
public static void main(String[] args){
Outer7 outer7 = new Outer7();
outer7.fun();
}
}
-----------------------------------------------------------------------------------------------------------------------
匿名内部类在开发中的使用:
interface Person {
public abstract void study();
}
class PersonDemo{
Person person;// Person person = new.. .();
// PersonDemo(){}
PersonDemo(Person person){ //Person person = new Student();
this.person = person;
}
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){
// Student student = new Student();
// 创建一个PersonDemo的对象
// PersonDemo personDemo = new PersonDemo(student);
//使用匿名内部类的形式创建对象
Person person = new Person(){
@Override
public void study(){
System.out.println("好好学习,天天向上");
}
};
person.study();
//使用匿名内部类进行方法传参
PersonDemo personDemo = new PersonDemo(new Person(){
@Override
public void study(){
System.out.println("这是匿名内部类使用带参构造方法");
}
});
new PersonDemo(new Person(){
@Override
public void study(){
System.out.println("这是匿名内部类使用带参构造方法");
}
});fun(new Person(){
@Override
public void study(){
System.out.println("好好学习,天天向上2");
}
});
new PersonDemo(new Person(){
@Override
public void study(){
System.out.println("这是匿名内部类使用带参构造方法");
}
});person.study();
}
}
----------------------------------------------------------------------------------------------------------------------
interface Inter {void show();}
class Outer { //补齐代码}
class OuterDemo{
public static void main(String[] args){
Outer.method().show();
}
}
要求在控制台输出"HelloWorld"
interface Inter2{
void show();
}
class Outer8{
//1、根据main方法调用的代码推出第一个结论:method()是静态的,可以直接通过类名直接访问/调用
//2、根据main方法中调用完method()方法之后,还能继续调用方法,我们判定method()是有返回值的
//而再观察后发现,show()方法恰好是Inter2接口中的方法,所以返回值类型是接口Inter2类型
//根据调用method()方法的参数是空,所以判定method()方法没有参数
public static Inter2 method(){
// return new Inter();
// Student student = new Student();
// return student;
return new Inter2(){
@Override
public void show(){
System.out.println("HelloWorld");
}
};
}
}
//class Student implements Inter2{
// @Override
// public void shou(){
// System.out.println("HelloWorld");
// }
//}
public class InnerClassDemo9{
public static void main(String[] args){
Outer8.method().show();
}
}
三、API常用类
1、相对路径:将该项目作为根目录(com.shujia.wyh.day13.changyonglei.Student)
绝对路径/完整路径:带上盘符(D:\IdeaProjects\bigdata15\src\com\shujia\wyh\day13\changyonglei\Student.java)
Object:Class Object是类Object结构的根,每个class都有Object作为超类
所有对象(包括数组)都实现了这个类的方法。Java中每个类都直接或间接继承了Object类
Object类中的方法:
public int hashCode()返回对象的哈希码值
支持这种方法是为了散列表,如HashMap提供的那样
注意:这里的哈希码值是根据哈希算法计算出来的一个值。这个值和地址有关系,但是这里返回的地址值并不是实际的地址值
你们现在就理解为地址值的另外一种表现形式
public final Class getClass()//返回的是该对象的类对象
返回Object的运行时类。返回的类对象是被表示类static synchronized方法所定的对象
public class StudentTest{
public static void main(String[] args){
Student s1 = new Student();
System.out.println(s1.hashCode());//1163157884
Student s2 = new Student();
System.out.println(s2.hashCode());//1956725890
Student s3 = s1;
System.out.println(s3.hashCode());//1163157884
System.out.println("======================================");
Student s4 = new Student();
System.out.println(s4.getClass());//class com.shujia.wyh.day13.changyonglei.Student
Class studentClass = s4.getClass();
//返回由类对象表示的实体名称(类,接口,数组类,原始类型或void),作为String
System.out,println(studentClass.getName());//com.shujia.wyh.day13.changyonglei.Student
//链式编程
System.out.println(s4.getClass().get.Name());//com.shujia.syh.day13.changyonglei.Student
}
}
2、Student4实现了标记接口Cloneable,表示这个允许被克隆
import java.until.Objects;
//Student4实现了标记接口Cloneable,表示这个允许被克隆
public class Student4 extends Object implements Cloneable{
private String name;
private int age;
private Demo demo;
public Student4(){
}
public Student4(String name,int age,Demo demo){
this.name = name;
this.age = age;
this.demo = demo;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public demo getDemo(){
this.demo = demo;
}
// @Override
// public String toString(){
@Override
public String toString(){
return "Student4{" +
"name = '"+name + '\' +
",age = " +age +
",demo = "+ demo +
'}';
}
// return"姓名:" + name + ",年龄" +age;
}
// s1.equals(s2)
@Override
public boolean equals(Object o){
//this -- s1
// o -- s2
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
Student4 student3 = (Student4) o;
retuen age == student3.age && Object.equals(name,student3.name);
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
-----------------------------------------------------------------------------------------------
public String toString()返回对象的字符串表示形式
一般来说,toString方法返回一个"textually代表"这个对象的字符串
结果应该是一个简单明了的表达,容易让人阅读,建议所有子类覆盖此方法
该toString类方法Object返回一个由其中的对象是一个实例,该符号字符的类的名称的字符串@和对象的哈希码的无符号的十六进制表示。换句话说,这个方法返回一个等于下列值的字符串:
getClass().getName() + '@' + Inter.toHecString(hashCode())
Integer:
public static String toHexString(int i)
作为整数参数的字符串表示形式,作为十六位中的无符号整数。将哈希值转化为一个地址值
我们虽然掌握了toString的方法使用,但是呢打印的一个结果是一个我们看不懂的地址值,换句话我们拿到这个结果没有意义
返回对象的字符串表示形式,实际上我们更想去看的是该对象中各个成员变量的值
恰好toString()方法是被public修饰的,也恰好它的返回值类型是String类型的,所以我们可以在其它类中对它做重写
今后无特殊情况不需要自己手写,自动生成即可
一个标准类的4.0版本:
成员变量:使用private关键字修饰
构造方法:一个无参。一个带所有参数的方法
成员方法:setXxx(...)/getXxx(...)
toString():自动生成即可,替代掉我们之前show方法。
public class StudentTest2{
public static void main(String[] name){
Student2 s = new Student2();
System.out.println(s.toString());//com.shujia.wyh.day13.changyonglei.Student2@4554617c
System.out.println("===============================================");
System.out.println(s.getClass().getName());//com.shujia.wyh.day13.changyonglei.Studen2
System.out.println("=============================================");
//toString()等价于getClass().getName() + '@' + Integer.toHexString(hashCode())
// 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.hasaCode));
System.out.println("=================================================");
Student2 s2 = new Student2("王宇",18);
System.out.println(s2.toString());
}
}
--------------------------------------------------------------------------------------------------------------------------
import jdk.nashorn.internal.parser,JSONParser;
/*
publicboolean equals(Object obj)指示一些其他对象是否等于此
今后我们想要弄清楚一个方法的实现的时候,想要弄清楚结果是为什么的时候,看源码
将鼠标光标放置在要看的方法上,按住ctrl+鼠标左键查看源码
通过观察源码发现:Object类中的equals方法实现底层依旧是==
public boolean equals(Object obj){
return (this == obj);
}
而==比较引用数据类型的时候,比较是地址值,当地址值不一样的时候,返回的是false
==:
基本数据类型的时候:比较的两个值是否一样
引用数据类型的时候:比较的是两个对象的地址值是否一样
equals:只能比较的引用数据类型
实际开发的时候,调用equals方法更希望它比较的是成员变量的值的是否一样
所以我们应该在子类中进行重写
不需要我们动手,自动生成即可
总结:
子类若是没有重写equals方法,使用的是父类Object类中的方法,比较的是地址值
子类要是重写了equals方法,比较的是成员变量值是否相同
public class StudentTest3{
public static void main(String[] args){
Student3 s1 = new Student3("周家祥",18);
Student3 s2= new Student3("周家祥",18);
System.out.println(s1 == s2);//false
Student3 s3 = s1;
System.out.println(s1 == s3);//true
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
//虽然我们搞清楚了equals的比较方法,但是我们观察现实生活中,姓名一样,年龄一样,就说明是同一个人,应该返回的是true
Student3 s4 = new Student3("王宇",19);
System.out.println(s1.equals(s4));
}
}
---------------------------------------------------------------------------------------------------------------------
protected void finalize():
当垃圾收集确定不再有该对象的的引用时,垃圾收集器在对象上调用该对象
一个子类覆盖了处理系统资源或执行其他清理的finalize方法
你们就简单理解这个方法为用于垃圾回收的,什么时候回收,不确定
GC机制,标记法
protected Object clone()创建并返回此对象副本
执行特定的克隆工作
其他包中的子类要想使用被protected修饰的方法,使用super关键字调用
clone的方法Object执行特定的克隆工作
首先,如果此对象的类不实现接口Cloneable,则抛出CloneNotSupportedException
一个类要想使用clone(),就必须实现Cloneable接口
通过观察API发现Cloneable接口中没有常量,也没有抽象方法
今后看到类似于Cloneable一样,里面什么都没有的接口,我们称之为标记接口
【】拷贝再IT行业中常见两种:
浅拷贝:
浅拷贝是指我们拷贝出来的对象的内部引用类型变量和原来的对象内部引用类型变量的地址值是一样的(指向的是同一个对象)
但是整个拷贝出来的对象和新对象不是同一个地址值
深拷贝:
全部拷贝对象的内容,包括内存的引用类型也进行拷贝,拷贝的时候,重新创建一个对象,成员变量值和原来被拷贝的一样
但是后续再对拷贝后的引用数据类型变量做修改,不会影响到原来被拷贝的
public class Student4{
public static void main(String[] args) throws CloneNotSupportedException{
Demo demo = new Demo();
Student4 s1 = new Student4("周家祥",18,demo);
System.out.println(s1.toString());
//这里其实隐含了一个多态
Object obj = s1.clone();
System.out.println(obj.toString);
System.out.println(s1.toString);
//拷贝前的demo与拷贝后的demo的地址值做比较
//发现demo地址值是一样的
System.out.println(s1.getDemo().hashCode());
Student4 s4 = (Student4)obj;
System.out.println(s4.getDemo().hashCode());
//拷贝整体对象的地址值与拷贝后整体对象的地址值
//发现拷贝后的地址值与原来对象的地址值不一样
System.out.println(s1.hashCode());//1956725890
System.out.printn(obj.hashCode());//356573597
System.out.println("========================================");
//clone()的意义到底在哪里?
//要想搞明白这个意义,就得知道一个知识点(浅拷贝/深拷贝)
//clone()到底是浅拷贝还是深拷贝
//clone()是属于浅拷贝的
// System.out.println(obj.hashCode());
// System.out.println(s1.hashCode());
}
}