Внутренние классы Java и статические вложенные классы? Рекомендуем | Примечания по отладке Java

Java

Эта статья участвует в "Месяце тем Java - Заметки по отладке Java", подробности см.Ссылка на мероприятие

Вопрос: внутренние классы Java и статические вложенные классы?

В чем основное различие между внутренними классами и статическими вложенными классами в Java? Играет ли дизайн/реализация роль в выборе одного из них?

Высокий балл ответа:

Много очков знаний, действительно нужно написать, чтобы освоить!!! \color{purple}Много очков знаний, вам действительно нужно их записать, чтобы стать мастером!!!{~}

Учебник по Java говорит:

术语:嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。

В общем, большинство программистов используют термины «вложенный» и «внутренний» взаимозаменяемо, но я буду использовать правильный термин «вложенный класс», который охватывает как внутренние, так и статические аспекты.

Классы могут быть бесконечно вложенными, например, класс A может содержать класс B, который содержит класс C, который содержит класс D, и так далее. Однако редко бывает несколько уровней вложенности классов, что, как правило, является плохим дизайном.

Три причины для создания вложенных классов:

组织:有时将一个类分类到另一个类的命名空间中似乎是最明智的,尤其是当它不会在任何其他上下文中使用时
访问:嵌套类具有对其包含的类的变量/字段的特殊访问权限(确切地说,哪个变量/字段取决于嵌套类的类型,无论是内部类还是静态类)。
便利:再次为每个新类型创建一个新文件很麻烦,尤其是当该类型仅在一个上下文中使用时

В Java есть четыре вида вложенных классов. Короче говоря, они:

静态类:声明为另一个类的静态成员
内部类:声明为另一个类的实例成员
本地内部类:在另一个类的实例方法中声明
匿名内部类:类似于本地内部类,但编写为返回一次性对象的表达式

Позвольте мне уточнить.

статический класс

Статические классы проще всего понять, потому что они не имеют ничего общего с экземплярами содержащего их класса.

Статический класс — это класс, объявленный как статический член другого класса. Как и другие статические члены, этот класс на самом деле является просто вешалкой, которая использует содержащий класс в качестве своего пространства имен, например, класс Goat, объявленный в пакете pizza как статический член класса Rhino, называется pizza.Rhino.Goat.

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

Откровенно говоря, статические классы — очень бесполезная функция, потому что классы уже имеют пространство имен пакетов. Единственная действительно мыслимая причина для создания статического класса состоит в том, чтобы этот класс имел доступ к закрытым статическим членам содержащего его класса, но я считаю, что это довольно неубедительная причина существования функциональности статического класса.

внутренний класс

Внутренний класс — это класс, объявленный как нестатический член другого класса:

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

    private void jerry() {
        Goat g = new Goat();
    }
}

Как и статические классы, внутренние классы определяются по имени содержащего их класса pizza.Rhino.Goat, но внутри содержащего класса они могут быть идентифицированы по их простому имени. Однако каждый экземпляр внутреннего класса привязан к определенному экземпляру содержащего его класса: как описано выше, Jerry, созданный в Goat, неявно связан с экземпляром Rhino, который находится в Jerry. В противном случае мы делаем связанный экземпляр Rhino явным при создании экземпляра Goat:

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(Обратите внимание, что вы называете внутренний тип Goat в странном новом синтаксисе: Java выводит содержащий тип из части rhino. И да, новый rhino.Goat() также имеет для меня больше смысла.)

Итак, какая польза от этого? Экземпляры внутреннего класса могут обращаться к членам экземпляра содержащего экземпляра класса. Эти включающие члены экземпляра вызываются внутри внутреннего класса только через их простое имя, а не через this (это во внутреннем классе относится к экземпляру внутреннего класса, а не к связанному экземпляру содержащего класса):

public class Rhino {

    private String barry;

    public class Goat {
        public void colin() {
            System.out.println(barry);
        }
    }
}

Во внутреннем классе вы можете вызвать this, содержащий класс Rhino.this, и вы можете использовать его для ссылки на его члены, такие как Rhino.this.barry.

локальный внутренний класс

Локальный внутренний класс — это класс, объявленный в теле метода. Такой класс известен только в содержащем его методе, поэтому его можно создать и получить доступ к его членам только в содержащем его методе. Преимущество этого заключается в том, что экземпляр локального внутреннего класса связан с этим экземпляром и имеет доступ к конечным локальным переменным содержащего его метода. Когда экземпляр использует конечную локальную переменную содержащего его метода, эта переменная сохраняет значение, которое она имела при создании экземпляра, даже если переменная выходит за пределы области действия (на самом деле это ограниченная закрытая версия Java).

Поскольку локальный внутренний класс не является ни классом, ни членом пакета, он не объявляется с уровнем доступа. (Обратите внимание, однако, что его собственные члены имеют тот же уровень доступа, что и обычный класс.)

Если локальный внутренний класс объявлен в методе экземпляра, при создании экземпляра экземпляр внутреннего класса привязывается к экземпляру, удерживаемому содержащим методом this, поэтому к членам экземпляра содержащего класса можно получить доступ, как в экземпляре . внутренний класс. Локальные внутренние классы создаются только по их имени, например, локальный внутренний класс Cat создается как new Cat() вместо new this.Cat(), как можно было ожидать.

анонимный внутренний класс

Анонимные внутренние классы — это синтаксически удобный способ написания локальных внутренних классов. Чаще всего локальные внутренние классыбыть создан не более одного раза за раз, когда запускается содержащий его метод. Что ж, было бы здорово, если бы мы могли объединить определение локального внутреннего класса и его единственного экземпляра в удобную синтаксическую форму, а также было бы неплохо, если бы нам не приходилось придумывать имя для класса (менее бесполезное ) тем лучше. Ваш код содержит имя лучше). Анонимные внутренние классы допускают следующие две ситуации:

new *ParentClassName*(*constructorArgs*) {*members*}

Это выражение, которое возвращает новый экземпляр безымянного класса, расширяющего ParentClassName. Вы не можете предоставить свой собственный конструктор. Вместо этого неявно предоставляется функция, которая просто вызывает суперконструктор, поэтому предоставленные аргументы должны соответствовать суперконструктору. (Если родитель содержит более одного конструктора, он называется «самым простым» конструктором, «самый простой» определяется довольно сложным набором правил, которые не стоит подробно изучать, просто обратите внимание на то, что NetBeans или Eclipse сообщают вам о содержании. .)

Кроме того, вы можете указать интерфейс для реализации:

new *InterfaceName*() {*members*}

Такое объявление создает новый экземпляр безымянного класса, который расширяет Object и реализует InterfaceName. Точно так же вы не можете предоставить свой собственный конструктор. В этом случае Java неявно предоставляет конструктор без аргументов, ничего не делающий (поэтому в этом случае аргументов конструктора никогда не будет).

Даже если вы не можете предоставить конструктор для анонимного внутреннего класса, вы можете использовать блок инициализации (блок {}, размещенный вне любого метода), чтобы выполнить любую настройку, которую вы хотите.

Чтобы было ясно, анонимные внутренние классы — это просто менее гибкий способ создания локального внутреннего класса с экземпляром. Если вам нужен локальный внутренний класс, который реализует несколько интерфейсов или реализует методы интерфейса, расширяя какой-либо класс, отличный от Object, или указывая свой собственный конструктор, вы должны создать обычный внутренний класс, называемый локальным типом.

Статья переведена сАминь 2 несколько ярлыков соответственно 6 дорвей 75 колит стал 67 там-АС4 из 6, 2, 7, очевидно 2 ай-переполнение стека-ком.перевод.о боже/вопросы/7…

Предложение автора: на самом деле говорить, что статический внутренний класс очень хромой ~ я не согласен

Следует отметить одну вещь: внутренний класс не загружается при загрузке внешнего класса, а загрузка внутреннего класса зависит от загрузки внешнего класса.

Одноэлементный шаблон классического статического внутреннего класса

public class Singleton {
    private static class LazyHolder {    
       private static final Singleton INSTANCE = new Singleton(); // final修饰不可继承,方法不可重写    
     }    
     private Singleton (){}    
     public static final Singleton getInstance() {    
        return LazyHolder.INSTANCE;     // 内部类加载由jvm加载保证顺序,保证线程安全。
     } 

}

При просмотре исходного кода я наткнулся на пример использования статических внутренних классов:

Класс шаблона для создания и кодирования URI: в классе UriComponentsBuilder.

// 内部类
private static class CompositePathComponentBuilder implements PathComponentBuilder {
                // 内部类初始化
		private final LinkedList<PathComponentBuilder> builders = new LinkedList<>();
                   // 添加路径
                public void addPathSegments(String... pathSegments) {
			if (!ObjectUtils.isEmpty(pathSegments)) {
				PathSegmentComponentBuilder psBuilder = getLastBuilder(PathSegmentComponentBuilder.class);
				FullPathComponentBuilder fpBuilder = getLastBuilder(FullPathComponentBuilder.class);
				if (psBuilder == null) {
					psBuilder = new PathSegmentComponentBuilder();
					this.builders.add(psBuilder);
					if (fpBuilder != null) {
						fpBuilder.removeTrailingSlash();
					}
				}
				psBuilder.append(pathSegments);
			}
		}
}   

Когда внутренний класс загружается

      /**
	 * 默认构造函数。受保护以防止直接实例化。
	 * @see #newInstance()
	 * @see #fromPath(String)
	 * @see #fromUri(URI)
	 */
	protected UriComponentsBuilder() {
		this.pathBuilder = new CompositePathComponentBuilder();
	}

При использовании внутренних классовaddPathSegments

     // 将路径段附加到现有路径。每个路径段都可以包含* URI模板变量,并且不应包含任何斜杠
	@Override
	public UriComponentsBuilder pathSegment(String... pathSegments) throws IllegalArgumentException {
		this.pathBuilder.addPathSegments(pathSegments); // 
		resetSchemeSpecificPart();
		return this;
	}

Обновление, в этой статье я писал о внутреннем классе и статическом внутреннем классе, задействованном в пуле потоков ThreadPoolExecutor.Наггетс.Талант/пост/696234…

Добро пожаловать в мою колонкуStackOverFlow,Я просматриваю качественные вопросы и ответы и часто беру интервью!!! \color{red}Добро пожаловать, обратите внимание на мою колонку StackOverFlow, я выберу качественные вопросы и ответы, часто беру интервью!!!{~}

Есть новейшие и элегантные методы реализации, и я также напишу свое мнение об этом вопросе и ответе в конце статьи.\color{red} имеет самую последнюю и элегантную реализацию, и я также напишу свое мнение об этом вопросе и ответе в конце статьи{~}

Искренне благодарю красивых и красивых девушек за то, что увидели это.Если эта статья будет хорошо написана, если вы почувствуете, что есть что-то

Ставьте лайк 👍 Подписывайтесь ❤️ Делитесь 👥 Мне очень полезно с 8 пакетами пресса! ! !

Если в этом блоге есть какие-либо ошибки, пожалуйста, критикуйте и советуйте, это очень ценится! ❤️❤️❤️❤️