其他分享
首页 > 其他分享> > 内部类和泛型

内部类和泛型

作者:互联网

目录

内部类

1.内部类分类

a.成员内部类

1)成员内部类对象的创建

2)成员内部类能否定义静态属性

3)对外部类来说,可否在外部类的静态方法中使用成员内部类

b.静态内部类

1)创建静态内部类对象

2)静态内部类能否拥有成员变量

3)静态内部类能否访问外部类的成员变量

4)成员内部类与静态内部类小结

c.方法内部类

d.匿名内部类

概念

匿名内部类默认会继承一个类或实现一个接口

2.特点

1)内部类和外部类可以方便可以方便的互相访问彼此的private属性

2) 使用内部类可以曲线救国来实现“多继承”

3.内部类使用方法/规则

4.内部类的设计

泛型

1.泛型的引入

2.泛型的基本使用

使用泛型改造上面的point类

3.泛型方法

4.泛型接口

1)子接口仍然保留泛型

5.泛型相关总结


内部类

所谓内部类,就是将类结构的定义套在另一个类的内部

1.内部类分类

分四种:成员内部类;静态内部类;方法内部类;匿名内部类(Lambda表达式的前身)

eg:现实生活中处处存在内部类:汽车发动机和汽车-发动机这个类套在汽车类的内部【也属于一种封装(保护性)】

a.成员内部类

(类比成员方法:成员方法能访问静态域,不能拥有静态域(无法定义静态属性))

直接定义在类中,不加任何修饰符(static)定义的类,就是成员内部类--成员方法或属性

public class Outter {//外部类
    //发动机-私有内部类,对外部完全隐藏,只在类的内部使用
    private class Inner{

    }
}

内部类和外部类可以方便可以方便的互相访问彼此的private属性

1)成员内部类对象的创建

1)外部类的内部创建:就和使用其它类没有区别

内部类名称  内部类引用=new  内部类();

 2)外部类的外部创建--前提:内部类对外部可见

外部类名称.内部类  引用=new  外部类().new 内部类();
Outter.Inner inner=new Outter().new Inner();
inner.test();

2)成员内部类能否定义静态属性

不能,成员内部类必须要依赖于外部类,若成员内部类有静态属性(静态属性:没对象也能用),没有外部类对象也能访问了,矛盾。

3)对外部类来说,可否在外部类的静态方法中使用成员内部类

即可否在外部类main(主方法:静态方法-没对象也能用)中创建内部类对象?

这句话类比相当于在静态方法中调用成员变量,肯定不可以调用。外部类的静态方法中根本没有外部类的对象,没有外部类的对象如何调用成员内部类。

b.静态内部类

(类比静态方法:静态方法能访问静态域,不能访问成员域)

定义在类中,使用static修饰的内部类。静态内部类不需要依赖外部类对象就可以使用

1)创建静态内部类对象

1)外部类的内部

ublic class Outter1 {
    static class Inner{
    }
    public void test(){//外部类成员方法
        Inner inner=new Inner();
    }
    public static void main(String[] args) {//外部类静态方法
        Inner inner=new Inner();
    }
}

问题:why外部类普通方法和静态方法都能创建静态内部类对象?

分析:类比类中静态变量,类中的静态属性没有类的对象就能使用,所以类的静态方法可以调用,成员方法更可以。(没对象都能调更何况有对象)

2)外部类的外部

外部类.内部类 引用=new 外部类.内部类();
Outter1.Inner inner=new Outter1.Inner();

静态内部类就是一个普通的类,只是套在了一个类的内部而已。

2)静态内部类能否拥有成员变量

普通类可以定义自己的成员变量,静态内部类可以存在成员域,即可以拥有自己的成员属性,只是不能访问外部类的成员域而已。

public class Outter1 {
    //静态内部类就是个普通类,只是把它套在了outter1的里面而已
    static class Inner{
        static int num=10;
        int age=1;//成员属性
    }

3)静态内部类能否访问外部类的成员变量

不能。外部类的成员变量是有对象才能访问,此时内部类就没有外部类对象,因此无法直接访问。

4)成员内部类与静态内部类小结

1)成员内部类可以访问外部类的成员域和静态域,但是不能拥有静态域

2)不能在外部类的静态方法中使用成员内部类

3)静态内部类可以拥有成员域,但不能直接访问外部类的成员域【可以通过new一个外部类对象来访问】,但是静态域可以随便访问

c.方法内部类

直接定义在方法内部的类,不允许使用任何访问修饰符,对外部完全隐藏【类比局部变量,出了这个方法类就没了】

public class Outter2 {
    public void fun(){
        //方法内部类,出了这个方法就没了,不能出现任何访问修饰符和static
        class Inner{
            
        }
    }
}

 方法内部类无法定义static域。方法内部类中若使用了方法的形参,该形参为隐式的final声明(JDK8之后,JDK8之前方法内部使用了形参必须要使用final声明)。除此外,和成员内部类用法相同。

分析:Inner未使用num这个变量,若使用:

分析:报错:方法内部类使用了方法的形参,形参num现在就是隐式的final声明,值不能修改

不修改值就可以执行【即不进行+-等运算操作】

d.匿名内部类

(lambda表达式的前身【函数式编程】)

概念

匿名内部类是方法内部类的特殊版本,直接不写类名称。99%用在方法传参过程,匿名内部类遵从方法内部类所有要求,并多出相关要求:

匿名内部类默认会继承一个类或实现一个接口

继承普通类或抽象类都可以,一般是继承抽象类

1)普通方法【不使用匿名类方式的接口传参使用】

public class Outter3 {
    public static void fun(IMessage msg){
        msg.printMsg();
    }

    public static void main(String[] args) {
        IMessage msg=new IMessageImpl();
        fun(msg);
    }
}
interface IMessage{
    void printMsg();
}
class IMessageImpl implements IMessage{

    @Override
    public void printMsg() {
        System.out.println("普通用法");
    }
}

2)使用匿名内部类

public class Outter3 {
    public static void fun(IMessage msg){
        msg.printMsg();
    }

    public static void main(String[] args) {
        fun(new IMessage() {//不是创建了接口,是创建了个匿名内部类,只不过这个类实现了IMessage接口。里面覆写了printMsg方法
            //fun()括号中一大堆的这就是一个匿名内部类
            //等同于创建了一个类实现了IMessage接口,创建了该类的对象
            @Override
            public void printMsg() {
                System.out.println("匿名内部类的用法");
            }
        });
    }
}
interface IMessage{
    void printMsg();
}

 

2.特点

1)内部类和外部类可以方便可以方便的互相访问彼此的private属性

public class Outter {
    private String msg="outter类中的msg属性";
    //-----------------------------------------------------------------------
    private class Inner{
        private int num=10;
        public void test(){
            //直接访问外部类中的msg属性
            System.out.println(msg);
        }
    }
    //----------------------------------------------------------------------
    public void fun(){
        //通过内部类的对象访问内部类的私有属性
        Inner inner=new Inner();
        //访问Inner类的私有属性
        System.out.println(inner.num);
        inner.test();
    }

    public static void main(String[] args) {
        Outter outter=new Outter();
        outter.fun();
    }
}

2) 使用内部类可以曲线救国来实现“多继承”

class A{
     int numA=10;
}
class B{
     int numB=20;
}
public class E {
    class C extends A{ }
    class D extends B{ }
    public void test(){
        C c=new C();
        D d=new D();
        System.out.println(c.numA);
        System.out.println(d.numB);
    }

    public static void main(String[] args) {
        E e=new E();
        e.test();
    }
}

3.内部类使用方法/规则

1.成员内部类的创建需要依赖外部类对象,在没有外部类对象之前,无法创建成员内部类对象

eg:心脏就是成员内部类,在没有人体的情况下,是无法直接创建心脏这个对象

2.内部类是一个相对独立的实体,与外部类不是is a关系

3.内部类和外部类可以方便的访问彼此的私有域:内部类可以直接访问外部类的元素和方法(包括私有域),外部类必须通过内部类的对象来访问内部类的元素和方法(包括私有域)。

原因:内部类中隐藏了外部类对象

⭐⭐

public class Outter {
    private String msg="outter类中的msg属性";
    class Inner{
        private int num=10;
        private String msg="内部类的msg属性";
        public void test(){
            //直接访问外部类中的msg属性
            //此处直接访问了外部类的私有成员变量msg
            //成员变量得通过对象访问,但此处并没有。说明外部类对象默认被传入了内部类中了System.out.println(msg);[没有private String msg="内部类的msg属性";时,打印的是外部类的msg属性]
            System.out.println(Outter.this.msg);//Outter.this表示明确调用的是外部类的msg属性。取掉打印出的就是内部类的msg属性
            System.out.println(Outter.this);//outter.this是传入的隐藏外部类对象
        }
    }
    public void fun(){//fun方法中产生了内部类对象
        Inner inner=new Inner();
        inner.test();
    }
    public static void main(String[] args) {
        Outter outter=new Outter();//外部产生外部类对象
        outter.fun();
    }
}

4.内部类的设计

内部类的设计不是我们现在的首选,写数据结构代码时如链表,Node就是典型的内部类设计,可以使用成员内部类或静态内部类都可以,对外隐藏封装即可。

泛型

1.泛型的引入

坐标类Point{
  x
  y
}  

Object用于接收所有类型,有包装类的自动拆装箱,基本类型自动装箱变为Integer或Double让Object接收。

public class Point {
    private Object x;
    private Object y;

    public Object getX() {
        return x;
    }
    public void setX(Object x) {
        this.x = x;
    }
    public Object getY() {
        return y;
    }
    public void setY(Object y) {
        this.y = y;
    }
    public static void main(String[] args) {
        Point point=new Point();
        //自动装箱
        point.setX(10.1);
        point.setY(20.2);
        //自动拆箱
        double x=(double) point.getX();
        double y=(double) point.getY();
        System.out.println("x="+x+" y="+y);
        Point point1=new Point();
        point1.setX("东经101度");
        point1.setY("北纬55度");
        String x1= (String) point1.getX();
        String y1=(String) point1.getY();
        System.out.println("x1="+x1+" y1="+y1);
    }
}

隐藏风险:在强制类型转换处:此时x和y都是相同类型,若用户输入的x,y的类型不同,在强转时就会发生错误。

类型转换异常:

当x,y不小心设置为不同的类型时,再强转时就会发生运行时异常(类型转换异常),强转时发生的,这个错误在编译期间无法发现。->泛型应运而生

2.泛型的基本使用

守门员:在编译器检查类型是否正确

所谓泛型,就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象时)再进行类型的定义。

public class MyClass<T> {
    /**
     * <>叫类型参数的声明
     * 类声明后的<>中的T被称为类型参数,用于指代任意类型⭐⭐
     * 实际上这个T只是个代表,写啥都行,表示此时value1,value2都是在类定义时没有明确类型,只有在使用时才告知编译器类型。
     * 出于规范而言,类型参数用单个的大写字母来代替
     * T:代表任意类
     * E:表示Element的意思,或是异常
     * K;V:K与V搭配使用:Map(Integer,String)
    */
    T value1;
    T value2;
    }

分析:这里就相当于把T换为Integer类型了,如果用了其他类型直接编译就会报错,而不是在运行时报错。若此时value1和value2的类型不一定想通过,则定义多个类型参数:

使用泛型改造上面的point类

/**
 * 泛型坐标类
 * 此时x,y是相同类型,使用一个类型参数(校验时就能发现出现类型错误)
 */
public class Point<T> {
    private T x;
    private T y;
    public T getX() {
        return x;
    }
    public void setX(T x) {
        this.x = x;
    }
    public T getY() {
        return y;
    }
    public void setY(T y) {
        this.y = y;
    }
    public static void main(String[] args) {
        Point<String> point=new Point<>();
        point.setX("东经120度");
        point.setY("北纬20度");
        String x= point.getX();//无需强转
        String y= point.getY();
    }
}

1)当创建Point对象时设置的类型和实际存储类型不同时,编译会报错,程序无法启动,提前将问题暴露在编译期间。

2)泛型不光在编译期就会校验类型以外,使用泛型还不需要强制类型转换。

3.泛型方法

看到<>,表示这是一个泛型的声明,就和class是类的声明,interface是接口的声明一样

泛型方法中的T和类中泛型参数T是两码事。

泛型方法始终以自己的类型参数为准,和类中的类型参数无关,为了避免混淆,一般定义泛型方法时,尽量避免使用类中使用过的类型参数字母。

public class MyClass<T,E> {
    //T,E代表两种不同的类型参数,在具体使用时可以相同也可以不同
    T value1;
    E value2;

    /**
     * 泛型方法
     */
    public <S> S test(S s){
        System.out.println(s);
        return s;
    }
    public static void main(String[] args) {
        MyClass<Integer,String> myClass=new MyClass<>();
        myClass.value1=10;
        myClass.test("哇哦");
    }
}

⭐⭐

4.泛型接口

public interface INews<T> {
    //返回值是T类型 T test(T t);
    T getNews(T t);
}

子类NewsImpl在实现接口时有两种选择:要么继续保留泛型,要么定义子类时明确类型

1)子接口仍然保留泛型

public class NewsImpl<T> implements INews<T>{
    @Override
    public T getNews(T t) {
        return null;
    }
}

2)明确类型(接收String类型)

public class NewsImpl2<String> implements INews<String>{
    @Override
    public String getNews(String string) {
        return null;
    }
}

5.泛型相关总结

1)看到<T>就是泛型声明,T称为类型参数,泛型指的是定义类或方法时参数或属性的类型不确定,只有当真正运行时,才确定的一种方式。

2)泛型方法始终以自己的类型参数为准,而且可以独立于泛型类存在。普通类中也可以定义一个泛型方法。

3)泛型接口,子类要么继续保留泛型:class MessageImpl<T> implements IMessage<T>;或者在实现子类时明确泛型的类型,子类就是普通类。

标签:部类,内部,void,class,泛型,类和泛,public
来源: https://blog.csdn.net/m0_58006481/article/details/122812626