栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

内部类和泛型

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

内部类和泛型

目录

内部类

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 value1;
    T value2;
    }

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

使用泛型改造上面的point类
public class Point {
    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 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 value1;
    E value2;

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

⭐⭐

4.泛型接口
public interface INews {
    //返回值是T类型 T test(T t);
    T getNews(T t);
}

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

1)子接口仍然保留泛型
public class NewsImpl implements INews{
    @Override
    public T getNews(T t) {
        return null;
    }
}

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

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

5.泛型相关总结

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

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/733183.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号