内部类和泛型
作者:互联网
目录
1)内部类和外部类可以方便可以方便的互相访问彼此的private属性
内部类
所谓内部类,就是将类结构的定义套在另一个类的内部
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