Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабораторная работа №2

.pdf
Скачиваний:
58
Добавлен:
31.05.2015
Размер:
437.57 Кб
Скачать

Лабораторная работа №2

Вложенные и внутренние классы

1.1 Понятие вложенного класса

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

класса, в который он вложен. Однако внешний класс не имеет доступа к членам вложенного класса. Вложенный класс, который объявлен непосредственно внутри области видимости своего внешнего класса, является его членом.

Можно также объявлять вложенные классы, являющиеся локальными для блока.

Существует два типа вложенных классов: статические и нестатические.

Статический вложенный класс — класс, к которому применен модификатор static. Поскольку он является статическим, должен обращаться к нестатическим членам своего внешнего класса при помощи объекта. То есть он не может непосредственно ссылаться на нестатические члены своего внешнего класса. Из-за этого ограничения статические вложенные классы используются редко.

Наиболее важный тип вложенного класса — внутренний класс.

Внутренний класс — что нестатический вложенный класс. Он имеет доступ ко всем переменным и методам своего внешнего класса и может непосредственно ссылаться на них так же, как это делают остальные нестатические члены внешнего класса.

1.2 Внутренние (inner) классы

Нестатические вложенные классы принято называть внутренними

(inner) классами. Доступ к элементам внутреннего класса возможен из внешнего класса только через объект внутреннего класса, который должен быть

1

создан в коде метода внешнего класса. Объект внутреннего класса всегда ассоциируется (скрыто хранит ссылку) с создавшим его объектом внешнего класса – так называемым внешним (enclosing) объектом. Внешний и внутренний классы могут выглядеть, например, так:

public class Ship {

//поля и конструкторы

//abstract, final, private, protected - допустимы

public class Engine { // определение внутреннего класса

// поля и методы public void launch() {

System.out.println("Запуск двигателя");

}

}// конец объявления внутреннего класса public void init() {// метод внешнего класса

// объявление объекта внутреннего класса

Engine eng = new Engine(); eng.launch();

}

}

При таком объявлении объекта внутреннего класса Engine в методе внешнего класса Ship нет реального отличия от использования какого-либо другого внешнего класса, кроме объявления внутри класса Ship.

Использование объекта внутреннего класса вне своего внешнего класса возможно только при наличии доступа (видимости) и при объявлении ссылки в виде:

Ship.Engine obj = new Ship().new Engine();

Основное отличие от внешнего класса состоит в больших возможностях ограничения видимости внутреннего класса по сравнению с обычным внешним классом. Внутренний класс может быть объявлен как private, что обеспечивает его полную невидимость вне класса-владельца и надежное сокрытие реализации. В этом случае ссылку obj, приведенную выше, объявить было бы нельзя. Создать объект такого класса можно только в методах и логических блоках внешнего класса. Использование protected позволяет

2

получить доступ к внутреннему классу для класса в другом пакете,

являющегося суперклассом внешнего класса.

Методы внутреннего класса имеют прямой доступ ко всем полям и методам внешнего класса, в то же время внешний класс может получить доступ к содержимому внутреннего класса только после создания объекта внутреннего класса. Внутренние классы не могут содержать статические атрибуты и методы, кроме констант (final static).

Простой пример практического применения взаимодействия класса-

владельца и внутреннего нестатического класса проиллюстрирован на следующем примере.

package example01; public class Student {

private int id;

private ExamResult[] exams; public Student(int id) {

this.id = id;

}

private class ExamResult {// внутренний класс private String name;

private int mark; private boolean passed;

public ExamResult(String name) { this.name = name;

passed = false;

}

public void passExam() { passed = true;

}

public void setMark(int mark) { this.mark = mark;

}

public int getMark() { return mark;

}

public int getPassedMark() {

final int PASSED_MARK = 4;// «волшебное» число return PASSED_MARK;

}

3

public String getName() { return name;

}

public boolean isPassed() { return passed;

}

} // окончание внутреннего класса

public void setExams(String[] name, int[] marks) { if (name.length != marks.length){

throw new IllegalArgumentException();

}

exams = new ExamResult[name.length]; for (int i = 0; i < name.length; i++) {

exams[i] = new ExamResult(name[i]); exams[i].setMark(marks[i]);

}

if (exams[i].getMark() >= exams[i].getPassedMark()){ exams[i].passExam();

}

}

public String toString() {

String res = "Студент: " + id + "\n"; for (int i = 0; i < exams.length; i++){

if (exams[i].isPassed()){

res += exams[i].getName() + " сдал \n";

}

else {

res += exams[i].getName() + " не сдал \n";

}

}

return res;

}

}

package example01;

public class AnySession {

public static void main(String[] args) { Student stud = new Student(822201);

String ex[] = {"Meханика","Программирование"}; int marks[] = { 2, 9 };

stud.setExams(ex, marks); System.out.println(stud);

}

}

4

В результате будет выведено:

Студент: 822201

Meханика не сдал

Программирование сдал

Внутренний класс определяет сущность предметной области ―результат экзамена‖ (класс ExamResult), которая обычно непосредственно связана в информационной системе с объектом класса Student. Класс ExamResult в

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

1.3 Вложенные (nested) классы

Если не существует необходимости в связи объекта внутреннего класса с объектом внешнего класса, то есть смысл сделать такой класс статическим.

Вложенный класс логически связан с классом-владельцем, но может быть использован независимо от него.

При объявлении такого внутреннего класса присутствует служебное слово static, и такой класс называется вложенным (nested). Если класс вложен в интерфейс, то он становится статическим по умолчанию. Такой класс способен наследовать другие классы, реализовывать интерфейсы и являться объектом наследования для любого класса, обладающего необходимыми правами доступа. В то же время статический вложенный класс для доступа к нестатическим членам и методам внешнего класса должен создавать объект внешнего класса, а напрямую имеет доступ только к статическим полям и методам внешнего класса. Для создания объекта вложенного класса объект внешнего класса создавать нет необходимости. Подкласс вложенного класса не способен унаследовать возможность доступа к членам внешнего класса,

которыми наделен его суперкласс.

5

package example02; public class Ship {

private int id;

// abstract, final, private, protected - допустимы public static class LifeBoat {

public static void down() { System.out.println("шлюпки на воду!");

}

public void swim() {

System.out.println("отплытие шлюпки");

}

}

}

}

package example02;

public class RunnerShip {

public static void main(String[] args) { // вызов статического метода

Ship.LifeBoat.down();

// создание объекта статического класса

Ship.LifeBoat lf = new Ship.LifeBoat();

// вызов обычного метода lf.swim();

}

}

Статический метод вложенного класса вызывается при указании полного относительного пути к нему. Объект lf вложенного класса создается с использованием имени внешнего класса без вызова его конструктора.

6

Пример

Определить класс Payment (покупка) с внутренним классом, с помощью

объектов которого можно сформировать покупку из нескольких товаров.

package task_3;

import java.io.IOException; import java.util.Scanner;

public class Payment

{

//название покупки private String name;

//перечень товаров

private Product [] prodArray;

//стоимость покупки private int cost;

//внутренний класс private class Product

{

// название товара

private String productName;

//стоимость товара private int productCost;

//конструктор по умолчанию public Product()

{

super(); productName = ""; productCost = 0;

}

//конструктор с параметрами

public Product(String productName, int productCost)

{

super();

this.productName = productName; this.productCost = productCost;

}

// методы внутреннего класса public String getProductName()

{

return this.productName;

}

public int getProductCost()

{

return this.productCost;

}

}

7

//конструктор по умолчанию public Payment()

{

super(); this.name = ""; this.cost = 0;

}

//конструктор с параметрами public Payment(String name)

{

super(); this.name = name;

}

public void setPayment() throws IOException

{

this.cost = 0;

System.out.print("Введите количество товаров, которое Вы хотите” + + “приобрести: ");

Scanner br = new Scanner(System.in); try

{

int dim = br.nextInt(); prodArray = new Product[dim];

for(int i = 0; i < dim; i++)

{

System.out.println("Товар " + (i+1) + ": "); System.out.print("Наименование: ");

String str_name = br.next(); System.out.print("Цена: "); int prod_cost = br.nextInt();

prodArray[i] = new Product(str_name, prod_cost); this.cost = this.cost + prodArray[i].productCost;

}

}

catch(NumberFormatException e)

{

System.out.println("Неверный формат");

}

catch(NegativeArraySizeException e)

{

System.out.println("Размерность массива не может быть < 0");

}

catch (NullPointerException e)

{

System.out.println(); System.out.println("Массив не создан");

}

}

// печать чека

public void printCheque() throws IOException

{

try

{

if(this.prodArray.length != 0)

{

System.out.println("============================"); System.out.println(" " + this.name); System.out.println("============================");

8

for(int i = 0; i < this.prodArray.length; i++)

{

System.out.printf("%3d", i+1); System.out.printf("%15s",

this.prodArray[i].productName); System.out.printf("%10d",

this.prodArray[i].productCost); System.out.println();

}

System.out.println("============================"); System.out.print("Общая стоимость: "); System.out.printf("%11d", this.cost); System.out.println(); System.out.println("============================");

}

else

{

System.out.println(); System.out.println("Массив не создан");

}

}

catch(NullPointerException e)

{

System.out.println(); System.out.println("Массив не создан");

}

}

}

package task_3;

import java.io.IOException; import task_3.Payment;

public class PaymentMain

{

public static void main(String [] args) throws IOException

{

try

{

Payment pay1 = new Payment("Первая покупка"); pay1.setPayment();

Payment pay2 = new Payment("Вторая покупка"); pay2.setPayment();

pay1.printCheque();

pay2.printCheque();

}

catch(NumberFormatException e)

{

System.out.println("Неверный формат");

}

catch(NullPointerException e)

{

System.out.println("Массив не создан");

}

}

}

9

Результат выполнения программы:

Введите количество товаров, которое Вы хотите приобрести: 3 Товар 1:

Наименование: Молоко Цена: 10000

Товар 2: Наименование: Яйца Цена: 14000

Товар 3: Наименование: Колбаса Цена: 87900

Введите количество товаров, которое Вы хотите приобрести: 2 Товар 1:

Наименование: Чай Цена: 45600

Товар 2: Наименование: Кофе Цена: 89000

============================

Первая покупка

============================

1

Молоко

10000

2

Яйца

14000

3

Колбаса

87900

============================

Общая стоимость: 111900

============================

============================

Вторая покупка

============================

1

Чай

45600

2

Кофе

89000

============================

Общая стоимость: 134600

============================

10