Résumé :
L'héritage multiple signifie qu'une classe peut hériter de plusieurs classe en même temps Une classe parent hérite de comportements et de caractéristiques. Cependant, nous savons que Java n'autorise qu'un seul héritage afin d'assurer la sécurité des données . Mais parfois, nous avons besoin de parvenir à un héritage multiple, et de telles situations existent réellement dans la vie réelle, comme l'héritage : nous héritons à la fois du comportement et des caractéristiques de notre père et de ceux de notre mère. Heureusement, Java nous offre deux façons d'implémenter l'héritage multiple de manière tortueuse : les interfaces et les classes internes. En fait, implémenter l'héritage multiple est une application extrêmement importante des classes internes. De plus, les classes internes peuvent également être bien cachées (par exemple, les classes internes des membres privés). Il existe quatre types de classes internes, à savoir les classes internes membres, les classes internes statiques, les classes internes locales et les classes internes anonymes. En particulier, la classe interne
membre : la classe interne membre est membre de la classe externe et est attachée à la classe externe , donc l'objet de classe interne ne peut être créé que si l'objet de classe externe est créé en premier. C'est pour cette raison que les classes internes membres ne peuvent pas contenir de variables et de méthodes
La classe interne statique est une classe interne modifiée en statique. L'objet de classe interne ne dépend pas de l'objet de classe externe , ce qui signifie que nous pouvons créer directement l'objet de classe interne, mais ses Tous les membres statiques et les méthodes statiques des classes externes ne sont accessibles que directement
Les classes internes locales sont les mêmes ; comme les classes internes des membres sont compilées, mais que leur portée a changé , elles ne peuvent être utilisées que dans cette méthode et cet attribut , et seront invalides en dehors de cette méthode et de cet attribut
Le principe de la définition d'une classe interne anonyme est que la classe interne doit hériter d'une classe ou implémenter une interface, le le format est nouvelle classe parent ou interface () {définir le contenu de la sous-classe (comme fonction , etc.)}. En d’autres termes, ce que la classe interne anonyme nous fournit en fin de compte est un objet d’une sous-classe anonyme.
1. Aperçu des classes internesLa classe interne est un concept au moment de la compilation. Une fois compilées avec succès, la classe interne et la classe externe deviendront deux classes complètement différentes. Par exemple, pour une classe externe nommée Outer et une classe interne nommée Inner définie à l'intérieur, une fois la compilation terminée, Outer.class et Outer$inner classeront deux classes. Par conséquent, les noms de variables/méthodes membres de la classe interne peuvent être les mêmes que ceux de la classe externe. Les classes internes peuvent être statiques ou peuvent être modifiées avec public, default, protected et private. En particulier, concernant la relation entre les noms de fichiers source Java et les noms de classes (la dénomination des noms de fichiers sources Java n'a rien à voir avec les classes internes, les classes et interfaces impliquées dans les trois règles suivantes font toutes référence à des classes externes. classes/interfaces ), doivent respecter les trois règles suivantes :
Dans un fichier source Java, s'il existe une classe publique ou une interface publique, il ne peut y avoir qu'une seule classe publique ou une seule interface publique, et il ne peut pas y avoir plusieurs classes ou interfaces publiques. Bien entendu, un fichier source Java peut avoir plusieurs classes ou interfaces visibles par le package, c'est-à-dire le modificateur d'accès par défaut (il n'y a pas de modificateur d'accès avant le nom de la classe). L'ordre des classes publiques (interfaces) et des classes visibles par le package (interfaces) dans le fichier peut être arbitraire, c'est-à-dire que la classe publique (interface) n'a pas besoin d'être en première position.
Si le fichier source Java ne contient pas de classe publique (interface publique), il n'y a pas de limite sur le nom du fichier source Java.
Tant qu'il est conforme à la convention de dénomination du nom de fichier, il n'a pas besoin d'avoir le même nom que n'importe quelle classe ou interface du fichier. Bien sûr, il peut également avoir le même. nom comme l’un d’eux.
Les noms des classes et des interfaces ne peuvent pas entrer en conflit.
Toute classe ou interface du même package ne peut pas porter le même nom. Les classes ou interfaces de différents packages peuvent porter le même nom car elles peuvent être distinguées par les packages.
2. Le rôle des classes internes
L'utilisation de classes internes peut nous apporter les avantages suivants :
Les classes internes peuvent être bien cachées (les classes générales non internes ne sont pas autorisées à avoir des autorisations privées et protégées, mais les classes internes le peuvent
La classe interne a des droits d'accès à tous les éléments de la classe externe
peut implémenter l'héritage multiple ; >
Pour le client, nous pouvons obtenir une instance InterfaceTest via la méthode getInnerInstance() de l'exemple, mais nous ne savons pas comment cette instance est implémentée. Vous ne pouvez pas ressentir l'existence de la classe d'implémentation spécifique correspondante. Puisque InnerClass est privé, si nous ne regardons pas le code source, nous ne pouvons même pas voir le nom de la classe spécifique qui implémente cette interface, donc les classes internes peuvent être bien cachées.
//测试接口public interface InterfaceTest { public void test(); }//外部类public class Example { //内部类 private class InnerClass implements InterfaceTest{ @Override public void test() { System.out.println("I am Rico."); } } //外部类方法 public InterfaceTest getInnerInstance(){ return new InnerClass(); } }//客户端public class Client { public static void main(String[] args) { Example ex = new Example(); InterfaceTest test = ex.getInnerInstance(); test.test(); } }/* Output: I am Rico. *///:~
2) La classe interne a des droits d'accès à tous les éléments de la classe externe
name Cette variable membre est une variable privée définie dans l'exemple Cette variable. est dans Les classes internes sont accessibles sans condition.
//外部类public class Example { private String name = "example"; //内部类 private class Inner{ public Inner(){ System.out.println(name); // 访问外部类的私有属性 } } //外部类方法 public Inner getInnerInstance() { return new Inner(); } }//客户端public class Client { public static void main(String[] args) { Example ex = new Example(); ex.getInnerInstance(); } }/* Output: example *///:~3) L'héritage multiple peut être obtenu
Pour l'héritage multiple, on peut dire que les interfaces ne résolvent qu'une partie du problème, tandis que les classes internes permettent plusieurs héritage La solution devient plus complète. Les classes internes rendent le mécanisme d'héritage de Java plus complet et constituent la principale raison de l'existence des classes internes. Une classe en Java ne peut hériter que d'une seule classe, et son héritage multiple est implémenté à l'aide d'interfaces avant d'apprendre les classes internes. Mais parfois, l'utilisation des interfaces présente de nombreux inconvénients. Par exemple, si nous implémentons une interface, nous devons implémenter toutes les méthodes qu'elle contient ; et les classes internes peuvent faire en sorte que notre classe hérite de plusieurs classes concrètes ou
classes abstraites, à éviter. les limites des interfaces. Regardez l'exemple suivant : Notez que la classe MainExample contient deux classes internes Test1 et Test2. Parmi eux, la classe Test1 hérite de la classe Exemple1 et la classe Test2 hérite de la classe Exemple2. De cette façon, la classe MainExample possède les méthodes de la classe Exemple1 et de la classe Exemple2, réalisant ainsi indirectement un héritage multiple.
//父类Example1public class Example1 { public String name() { return "rico"; } }//父类Example2public class Example2 { public int age() { return 25; } }//实现多重继承的效果public class MainExample { //内部类Test1继承类Example1 private class Test1 extends Example1 { public String name() { return super.name(); } } //内部类Test2继承类Example2 private class Test2 extends Example2 { public int age() { return super.age(); } } public String name() { return new Test1().name(); } public int age() { return new Test2().age(); } public static void main(String args[]) { MainExample mexam = new MainExample(); System.out.println("姓名:" + mexam.name()); System.out.println("年龄:" + mexam.age()); } }/* Output: 姓名:rico 年龄:25 *///:~
4) Évitez de modifier l'interface et d'appeler deux méthodes portant le même nom dans la même classe
À l'heure actuelle, la méthode test() dans Test appartient-elle à la méthode test() qui couvre MyTest, ou implémente-t-elle la méthode test() dans InterfaceTest ? Comment pouvons-nous ajuster la méthode ici dans MyTest ? Il est évidemment difficile de faire la distinction. Et si nous utilisons des classes internes, ce problème peut être facilement résolu. Regardez le code suivant :
//Test 所实现的接口public interface InterfaceTest { public void test(); }//Test 所实现的类public class MyTest { public void test(){ System.out.println("MyTest"); } }//不使用内部类的情形public class Test extends MyTest implements InterfaceTest{ public void test(){ System.out.println("Test"); } }
En utilisant une classe interne pour implémenter l'interface, elle n'entrera pas en conflit avec la méthode du même nom héritée par la classe externe.
//Test 所实现的接口public interface InterfaceTest { public void test(); }//Test 所实现的类public class MyTest { public void test(){ System.out.println("MyTest"); } }//使用内部类的情形public class AnotherTest extends MyTest { private class InnerTest implements InterfaceTest { @Override public void test() { System.out.println("InterfaceTest"); } } public InterfaceTest getCallbackReference() { return new InnerTest(); } public static void main(String[] args) { AnotherTest aTest = new AnotherTest(); aTest.test(); // 调用类MyTest 的 test() 方法 aTest.getCallbackReference().test(); // 调用InterfaceTest接口中的 test() 方法 } }
3. Types de classes internes
(2) Définir une classe (classe interne locale, classe interne anonyme) dans la méthode.
2. Classe interne membre
La classe interne membre est attachée à la classe externe, donc l'objet de classe interne ne peut être créé que si l'objet de classe externe est créé en premier. C'est pour cette raison que les classes internes membres ne peuvent pas contenir de variables et de méthodes statiques , voir l'exemple suivant : 如果上面的代码编译无误, 那么我们就可以直接通过 Outter.Inner.a 拿到内部类Inner的实例。 由于内部类的实例一定要绑定到一个外部类的实例的,所以矛盾。因此,成员内部类不能含有 static 变量/方法。此外,成员内部类与 static 的关系还包括: 2、交互 成员内部类与外部类的交互关系为: 成员内部类可以直接访问外部类的所有成员和方法,即使是 private 的; 外部类需要通过内部类的对象访问内部类的所有成员变量/方法。 对于代码片段 1和2,可以用来生成内部类的对象,这种方法存在两个小知识点需要注意: 1) 开头的 Out 是为了标明需要生成的内部类对象在哪个外部类当中; 因此,成员内部类,外部类和客户端之间的交互关系为: 在成员内部类使用外部类对象时,使用 outer.this 来表示外部类对象; 在外部类中使用内部类对象时,需要先进行创建内部类对象; 在客户端创建内部类对象时,需要先创建外部类对象。 特别地,对于成员内部类对象的获取,外部类一般应提供相应的 getxxx() 方法。 3、私有成员内部类 如果一个成员内部类只希望被外部类操作,那么可以使用 private 将其声明私有内部类。例如, 在上面的代码中,我们必须在Out类里面生成In类的对象进行操作,而无法再使用Out.In in = new Out().new In() 生成内部类的对象。也就是说,此时的内部类只对外部类是可见的,其他类根本不知道该内部类的存在。 1、定义与原理 静态内部类,就是修饰为 static 的内部类,该内部类对象不依赖于外部类对象,就是说我们可以直接创建内部类对象。看下面例子: 2、交互 静态内部类与外部类的交互关系为: 静态内部类可以直接访问外部类的所有静态成员和静态方法,即使是 private 的; 外部类可以通过内部类对象访问内部类的实例成员变量/方法;对于内部类的静态域/方法,外部类可以通过内部类类名访问。 3、成员内部类和静态内部类的区别 成员内部类和静态内部类之间的不同点包括: 静态内部类对象的创建不依赖外部类的实例,但成员内部类对象的创建需要依赖外部类的实例; 成员内部类能够访问外部类的静态和非静态成员,静态内部类不能访问外部类的非静态成员; 1、定义与原理 有这样一种内部类,它是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,但又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。 2、final 参数 对于final参数,若是将引用类型参数声明为final,我们无法在方法中更改参数引用所指向的对象;若是将基本类型参数声明为final,我们可以读参数,但却无法修改参数(这一特性主要用来向局部内部类和匿名内部类传递数据)。 如果定义一个局部内部类,并且希望它的方法可以直接使用外部定义的数据,那么我们必须将这些数据设为是 final 的;特别地,如果只是局部内部类的构造器需要使用外部参数,那么这些外部参数就没必要设置为 final,例如: 有时候我为了免去给内部类命名,便倾向于使用匿名内部类,因为它没有名字。匿名内部类的使用需要注意以下几个地方: 匿名内部类是没有访问修饰符的; 匿名内部类是没有构造方法的 (因为匿名内部类连名字都没有); 定义匿名内部类的前提是,内部类必须是继承一个类或者实现接口,格式为 new 父类或者接口(){子类的内容(如函数等)}。也就是说,匿名内部类最终提供给我们的是一个匿名子类的对象,例如: 若匿名内部类 (匿名内部类没有构造方法) 需要直接使用其所在的外部类方法的参数时,该形参必须为 final 的;如果匿名内部类没有直接使用其所在的外部类方法的参数时,那么该参数就不必为final 的,例如: 从上述代码中可以看到,当匿名内部类直接使用其所在的外部类方法的参数时,那么这些参数必须被设为 final的。为什么呢?本文所引用到的一篇文章是这样解释的: “这是一个编译器设计的问题,如果你了解java的编译原理的话很容易理解。首先,内部类被编译的时候会生成一个单独的内部类的.class文件,这个文件并不与外部类在同一class文件中。当外部类传的参数被内部类调用时,从java程序的角度来看是直接的调用,例如: 从代码来看,好像是内部类直接调用的a参数和b参数,但是实际上不是,在java编译器编译以后实际的操作代码是: 从以上代码来看,内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。这样就很容易理解为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。” 以上关于匿名内部类的每个例子使用的都是默认无参构造函数,下面我们介绍 带参数构造函数的匿名内部类: 特别地,匿名内部类通过实例初始化 (实例语句块主要用于匿名内部类中),可以达到类似构造器的效果,如下: 内部类的继承,是指内部类被继承,普通类 extents 内部类。而这时候代码上要有点特别处理,具体看以下例子: 可以看到,子类的构造函数里面要使用父类的外部类对象.super() [成员内部类对象的创建依赖于外部类对象];而这个外部类对象需要从外面创建并传给形参。public class Outter {
private class Inner {
private final static int x=1; // OK
/* compile errors for below declaration
* "The field x cannot be declared static in a non-static inner type,
* unless initialized with a constant expression" */
final static Inner a = new Inner(); // Error
static Inner a1=new Inner(); // Error
static int y; // Error
}
}
//外部类class Out { private int age = 12; private String name = "rico";
//内部类
class In { private String name = "livia";
public void print() {
String name = "tom";
System.out.println(age);
System.out.println(Out.this.name);
System.out.println(this.name);
System.out.println(name);
}
} // 推荐使用getxxx()来获取成员内部类的对象
public In getInnerClass(){ return new In();
}
}public class Demo {
public static void main(String[] args) {
Out.In in = new Out().new In(); // 片段 1
in.print(); //或者采用注释内两种方式访问
/*
* 片段 2
Out out = new Out();
out.getInnerClass().print(); // 推荐使用外部类getxxx()获取成员内部类对象
Out.In in = out.new In();
in.print();
*/
}
}/* Output:
12
rico
livia
tom
*///:~
2) 必须先有外部类的对象才能生成内部类的对象。
class Out { private int age = 12; private class In {
public void print() {
System.out.println(age);
}
} public void outPrint() { new In().print();
}
}public class Demo {
public static void main(String[] args) { /*
* 此方法无效
Out.In in = new Out().new In();
in.print();
*/
Out out = new Out();
out.outPrint();
}
}/* Output:
12
*///:~
三. 静态内部类
四. 局部内部类
// 例 1:定义于方法内部public class Parcel4 {
public Destination destination(String s) {
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Destination d = p.destination("Tasmania");
}
}
// 例 2:定义于作用域内部public class Parcel5 {
private void internalTracking(boolean b) {
if (b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
p.track();
}
}
五. 匿名内部类
// 例 1abstract class AbsDemo
{ abstract void show();
}public class Outer{
int x=3; public void function()//可调用函数
{ new AbsDwmo()//匿名内部类
{ void show()
{
System.out.println("x==="+x);
} void abc()
{
System.out.println("haha");
}
}.abc(); //匿名内部类调用函数,匿名内部类方法只能调用一次
}
}
// 例 2interface Inner { //注释后,编译时提示类Inner找不到
String getName();
}public class Outer {
public Inner getInner(final String name, String city) {
return new Inner() {
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
System.out.println(inner instanceof Inner); //匿名内部类实质上是一个匿名子类的对象
} /* Output:
Inner
true
*///:~ }
// 情形 1:匿名内部类直接使用其所在的外部类方法的参数 namepublic class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city) { // 形参 name 被设为 final
return new Inner() {
private String nameStr = name; // OK
private String cityStr = city; // Error: 形参 city 未被设为 final
public String getName() {
return nameStr;
}
};
}
}
// 情形 2:匿名内部类没有直接使用其所在的外部类方法的参数public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
}
//注意这里的形参city,由于它没有被匿名内部类直接使用,而是被抽象类Inner的构造函数所使用,所以不必定义为final
public Inner getInner(String name, String city) {
return new Inner(name, city) { // OK,形参 name 和 city 没有被匿名内部类直接使用
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
}
abstract class Inner {
Inner(String name, String city) {
System.out.println(city);
}
abstract String getName();
}
public void dosome(final String a,final int b){
class Dosome{ public void dosome(){
System.out.println(a+b)
}
};
Dosome some=new Dosome();
some.dosome();
}
class Outer$Dosome{
public Dosome(final String a,final int b){
this.Dosome$a=a;
this.Dosome$b=b;
}
public void dosome(){
System.out.println(this.Dosome$a+this.Dosome$b);
}
}
public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city) {
return new Inner(name, city) { //匿名内部类
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
}
abstract class Inner {
Inner(String name, String city) { // 带有参数的构造函数
System.out.println(city);
}
abstract String getName();
}
public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
System.out.println(inner.getProvince());
}
public Inner getInner(final String name, final String city) {
return new Inner() {
private String nameStr = name;
private String province;
// 实例初始化
{
if (city.equals("gz")) {
province = "gd";
}else {
province = "";
}
}
public String getName() {
return nameStr;
}
public String getProvince() {
return province;
}
};
}
}
六. 内部类的继承
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!