Java Language
Классы и объекты
Поиск…
Вступление
Объекты имеют состояния и поведение. Пример: у собаки есть состояния - цвет, имя, порода, а также поведение - виляние хвоста, лай, еда. Объект является экземпляром класса.
Класс. Класс может быть определен как шаблон / план, который описывает поведение / состояние, которое поддерживает объект его типа.
Синтаксис
- class Example {} // ключевое слово, имя, тело
Самый простой возможный класс
class TrivialClass {}
Класс состоит как минимум из ключевого слова class
, имени и тела, которое может быть пустым.
Вы создаете класс с помощью new
оператора.
TrivialClass tc = new TrivialClass();
Участник объекта против статического члена
С этим классом:
class ObjectMemberVsStaticMember {
static int staticCounter = 0;
int memberCounter = 0;
void increment() {
staticCounter ++;
memberCounter++;
}
}
следующий фрагмент кода:
final ObjectMemberVsStaticMember o1 = new ObjectMemberVsStaticMember();
final ObjectMemberVsStaticMember o2 = new ObjectMemberVsStaticMember();
o1.increment();
o2.increment();
o2.increment();
System.out.println("o1 static counter " + o1.staticCounter);
System.out.println("o1 member counter " + o1.memberCounter);
System.out.println();
System.out.println("o2 static counter " + o2.staticCounter);
System.out.println("o2 member counter " + o2.memberCounter);
System.out.println();
System.out.println("ObjectMemberVsStaticMember.staticCounter = " + ObjectMemberVsStaticMember.staticCounter);
// the following line does not compile. You need an object
// to access its members
//System.out.println("ObjectMemberVsStaticMember.staticCounter = " + ObjectMemberVsStaticMember.memberCounter);
производит этот вывод:
o1 static counter 3
o1 member counter 1
o2 static counter 3
o2 member counter 2
ObjectMemberVsStaticMember.staticCounter = 3
Примечание. Вы не должны вызывать static
члены на объектах, а на классах. Хотя это не имеет значения для JVM, читатели-люди оценят это.
static
члены являются частью класса и существуют только один раз для каждого класса. ОТСУТСТВИЯ static
члены существуют в случаях, существует независимая копия для каждого экземпляра. Это также означает, что вам нужен доступ к объекту этого класса для доступа к его членам.
Методы перегрузки
Иногда такие же функции должны быть написаны для разных типов входов. В то время можно использовать одно и то же имя метода с другим набором параметров. Каждый другой набор параметров известен как сигнатура метода. Как видно на примере, один метод может иметь несколько подписей.
public class Displayer {
public void displayName(String firstName) {
System.out.println("Name is: " + firstName);
}
public void displayName(String firstName, String lastName) {
System.out.println("Name is: " + firstName + " " + lastName);
}
public static void main(String[] args) {
Displayer displayer = new Displayer();
displayer.displayName("Ram"); //prints "Name is: Ram"
displayer.displayName("Jon", "Skeet"); //prints "Name is: Jon Skeet"
}
}
Преимущество состоит в том, что одна и та же функциональность вызывается с двумя разными количествами входов. При вызове метода в соответствии с входом мы передаем (в этом случае либо одно строковое значение, либо два строковых значения) выполняется соответствующий метод.
Методы могут быть перегружены:
На основании количества переданных параметров .
Пример:
method(String s)
иmethod(String s1, String s2)
.
На основе порядка параметров .
Пример:
method(int i, float f)
иmethod(float f, int i))
.
Примечание. Методы не могут быть перегружены путем изменения только типа возвращаемого значения ( int method()
считается таким же, как String method()
и при попытке будет RuntimeException
). Если вы измените тип возврата, вы также должны изменить параметры для перегрузки.
Строительство и использование базовых объектов
Объекты входят в свой класс, поэтому простым примером может служить автомобиль (подробные пояснения ниже):
public class Car {
//Variables describing the characteristics of an individual car, varies per object
private int milesPerGallon;
private String name;
private String color;
public int numGallonsInTank;
public Car(){
milesPerGallon = 0;
name = "";
color = "";
numGallonsInTank = 0;
}
//this is where an individual object is created
public Car(int mpg, int, gallonsInTank, String carName, String carColor){
milesPerGallon = mpg;
name = carName;
color = carColor;
numGallonsInTank = gallonsInTank;
}
//methods to make the object more usable
//Cars need to drive
public void drive(int distanceInMiles){
//get miles left in car
int miles = numGallonsInTank * milesPerGallon;
//check that car has enough gas to drive distanceInMiles
if (miles <= distanceInMiles){
numGallonsInTank = numGallonsInTank - (distanceInMiles / milesPerGallon)
System.out.println("Drove " + numGallonsInTank + " miles!");
} else {
System.out.println("Could not drive!");
}
}
public void paintCar(String newColor){
color = newColor;
}
//set new Miles Per Gallon
public void setMPG(int newMPG){
milesPerGallon = newMPG;
}
//set new number of Gallon In Tank
public void setGallonsInTank(int numGallons){
numGallonsInTank = numGallons;
}
public void nameCar(String newName){
name = newName;
}
//Get the Car color
public String getColor(){
return color;
}
//Get the Car name
public String getName(){
return name;
}
//Get the number of Gallons
public String getGallons(){
return numGallonsInTank;
}
}
Объектами являются экземпляры их класса. Таким образом, способ создания объекта был бы путем вызова класса Car одним из двух способов в вашем основном классе (основной метод в Java или onCreate в Android).
Опция 1
`Car newCar = new Car(30, 10, "Ferrari", "Red");
Вариант 1 - это то, где вы по существу рассказываете программе все о Автомобиле при создании объекта. Для изменения любого свойства автомобиля потребуется вызвать один из методов, например метод repaintCar
. Пример:
newCar.repaintCar("Blue");
Примечание. Убедитесь, что вы передали правильный тип данных методу. В приведенном выше примере вы также можете передать переменную методу repaintCar
если тип данных правильный .
Это был пример изменения свойств объекта, для получения свойств объекта потребовался бы метод из класса Car, у которого есть возвращаемое значение (что означает void
метод). Пример:
String myCarName = newCar.getName(); //returns string "Ferrari"
Вариант 1 - лучший вариант, когда у вас есть все данные объекта во время создания.
Вариант 2
`Car newCar = new Car();
Вариант 2 получает тот же эффект, но требует больше работы для правильного создания объекта. Я хочу напомнить об этом Конструкторе в классе Car:
public void Car(){
milesPerGallon = 0;
name = "";
color = "";
numGallonsInTank = 0;
}
Обратите внимание, что вам не нужно передавать какие-либо параметры в объект для его создания. Это очень полезно, когда у вас нет всех аспектов объекта, но вам нужно использовать те части, которые у вас есть. Это устанавливает общие данные в каждую из переменных экземпляра объекта, так что, если вы вызываете фрагмент данных, который не существует, ошибки не генерируются.
Примечание. Не забывайте, что вам нужно установить части объекта позже, после чего вы не инициализировали его. Например,
Car myCar = new Car();
String color = Car.getColor(); //returns empty string
Это распространенная ошибка среди объектов, которые не инициализируются всеми их данными. Ошибок избегали, потому что есть Конструктор, который позволяет создать пустой объект Car с переменными stand-in ( public Car(){}
), но никакая часть myCar не была настроена. Правильный пример создания объекта автомобиля:
Car myCar = new Car();
myCar.nameCar("Ferrari");
myCar.paintCar("Purple");
myCar.setGallonsInTank(10);
myCar.setMPG(30);
И, как напоминание, получить свойства объекта, вызвав метод в вашем основном классе. Пример:
String myCarName = myCar.getName(); //returns string "Ferrari"
Конструкторы
Конструкторы - это специальные методы, названные в честь класса и без возвращаемого типа, и используются для построения объектов. Конструкторы, подобно методам, могут принимать входные параметры. Конструкторы используются для инициализации объектов. Абстрактные классы также могут иметь конструкторы.
public class Hello{
// constructor
public Hello(String wordToPrint){
printHello(wordToPrint);
}
public void printHello(String word){
System.out.println(word);
}
}
// instantiates the object during creating and prints out the content
// of wordToPrint
Важно понимать, что конструкторы отличаются от методов несколькими способами:
Конструкторы могут принимать только модификаторы
public
,private
иprotected
и не могут быть объявленыabstract
,final
,static
илиsynchronized
.Конструкторы не имеют типа возврата.
Конструкторы ДОЛЖНЫ называться так же, как имя класса. В
Hello
например,Hello
имя конструктора объекта является таким же , как имя класса.this
ключевое слово имеет дополнительное использование внутри конструкторах.this.method(...)
вызывает метод в текущем экземпляре, тогда какthis(...)
ссылается на другой конструктор текущего класса с разными сигнатурами.
Конструкторы также можно вызывать через наследование, используя ключевое слово super
.
public class SuperManClass{
public SuperManClass(){
// some implementation
}
// ... methods
}
public class BatmanClass extends SupermanClass{
public BatmanClass(){
super();
}
//... methods...
}
См. Спецификацию Java Language # 8.8 и # 15.9
Инициализация статических конечных полей с использованием статического инициализатора
Чтобы инициализировать static final
поля, которые требуют использования более одного выражения, для назначения значения может использоваться static
инициализатор. В следующем примере инициализируется немодифицируемый набор String
s:
public class MyClass {
public static final Set<String> WORDS;
static {
Set<String> set = new HashSet<>();
set.add("Hello");
set.add("World");
set.add("foo");
set.add("bar");
set.add("42");
WORDS = Collections.unmodifiableSet(set);
}
}
Объяснение, что такое перегрузка и переопределение метода.
Переопределение и перегрузка метода - это две формы полиморфизма, поддерживаемые Java.
Перегрузка метода
Перегрузка метода (также известный как статический полиморфизм) - это способ, которым вы можете иметь два (или более) метода (функции) с одним и тем же именем в одном классе. Да, это так просто.
public class Shape{
//It could be a circle or rectangle or square
private String type;
//To calculate area of rectangle
public Double area(Long length, Long breadth){
return (Double) length * breadth;
}
//To calculate area of a circle
public Double area(Long radius){
return (Double) 3.14 * r * r;
}
}
Таким образом, пользователь может вызывать тот же метод для области в зависимости от типа формы, которую он имеет.
Но реальный вопрос заключается в следующем: как будет компилятор java отличить, какое тело метода должно быть выполнено?
Ну, Java ясно дал понять, что хотя имена методов ( area()
в нашем случае) могут быть одинаковыми, но метод аргументов должен быть другим.
Перегруженные методы должны иметь список разных аргументов (количество и типы).
При этом мы не можем добавить еще один метод для вычисления площади квадрата: public Double area(Long side)
потому что в этом случае он будет конфликтовать с методом области окружности и вызовет двусмысленность для java-компилятора.
Слава богу, есть некоторые релаксации при написании перегруженных методов, таких как
Может иметь разные типы возврата.
Могут быть разные модификаторы доступа.
Может бросать разные исключения.
Почему это называется статическим полиморфизмом?
Ну, это потому, что перегруженные методы должны быть вызваны, определяется во время компиляции, основываясь на фактическом количестве аргументов и типах аргументов времени компиляции.
Одной из распространенных причин использования перегрузки метода является простота кода, который он предоставляет. Например, помните
String.valueOf()
который принимает почти любой тип аргумента? То, что написано за сценой, возможно, примерно так:
static String valueOf(boolean b)
static String valueOf(char c)
static String valueOf(char[] data)
static String valueOf(char[] data, int offset, int count)
static String valueOf(double d)
static String valueOf(float f)
static String valueOf(int i)
static String valueOf(long l)
static String valueOf(Object obj)
Переопределение метода
Ну, переопределение метода (да, вы догадались, что это правильно, он также известен как динамический полиморфизм) является несколько более интересной и сложной темой.
При переопределении метода мы перезаписываем тело метода, предоставляемое родительским классом. Понял? Нет? Давайте рассмотрим пример.
public abstract class Shape{
public abstract Double area(){
return 0.0;
}
}
Таким образом, у нас есть класс под названием Shape, и у него есть метод, называемый областью, которая, вероятно, вернет область формы.
Скажем, теперь у нас есть два класса, называемые Circle и Rectangle.
public class Circle extends Shape {
private Double radius = 5.0;
// See this annotation @Override, it is telling that this method is from parent
// class Shape and is overridden here
@Override
public Double area(){
return 3.14 * radius * radius;
}
}
Аналогично, класс прямоугольника:
public class Rectangle extends Shape {
private Double length = 5.0;
private Double breadth= 10.0;
// See this annotation @Override, it is telling that this method is from parent
// class Shape and is overridden here
@Override
public Double area(){
return length * breadth;
}
}
Итак, теперь оба класса ваших детей обновили тело метода, предоставленное родительским ( Shape
) классом. Теперь вопрос заключается в том, как увидеть результат? Хорошо, давайте сделаем это старым способом psvm
.
public class AreaFinder{
public static void main(String[] args){
//This will create an object of circle class
Shape circle = new Circle();
//This will create an object of Rectangle class
Shape rectangle = new Rectangle();
// Drumbeats ......
//This should print 78.5
System.out.println("Shape of circle : "+circle.area());
//This should print 50.0
System.out.println("Shape of rectangle: "+rectangle.area());
}
}
Вот Это Да! разве это не здорово? Два объекта одного типа вызывают одни и те же методы и возвращают разные значения. Мой друг, это сила динамического полиморфизма.
Вот график, чтобы лучше сравнить различия между этими двумя:
Перегрузка метода | Переопределение метода |
---|---|
Перегрузка метода используется для повышения удобочитаемости программы. | Переопределение метода используется для обеспечения конкретной реализации метода, который уже предоставлен его суперклассом. |
Перегрузка метода выполняется внутри класса. | Переопределение метода происходит в двух классах, имеющих отношение IS-A (наследование). |
В случае перегрузки метода параметр должен быть другим. | В случае переопределения метода параметр должен быть таким же. |
Перегрузка метода является примером полиморфизма времени компиляции. | Переопределение метода - пример полиморфизма времени выполнения. |
В java перегрузка метода не может быть выполнена путем изменения типа возвращаемого метода. Тип возврата может быть таким же или другим при перегрузке метода. Но вам придется изменить параметр. | Тип возврата должен быть таким же или ковариантным в переопределении метода. |