Java for Beginner
673 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Всем доброго субботнего утра!🖐

Постов на сегодня нет, поэтому отдыхаем и расслабляемся!🤪😂

А я пойду готовить на завтра тему встречи - "Деплой приложения".

Надеюсь успеть🧐

Всем полезных выходных💪
Всем привет!

Для понимания ситуации проведу опрос - откуда Вы узнали о нашем телеграм-канале?
Anonymous Poll
14%
По поиску
16%
По каналу в YOUTUBE
3%
По каналу в RUTUBE
43%
Из JavaRush
5%
Друг посоветовал
19%
Я здесь так давно, что и не помню)
This media is not supported in your browser
VIEW IN TELEGRAM
Всем доброго утра! 🖐

Сегодня как и каждое воскресение в 16:00 по МСК мы собираемся чтобы попробовать что-то новое!

Сегодняшняя тема: Деплой приложений


Жду всех. Приходите будет интересно😉
Деплой приложений. Метод для контейнеризации приложений в Docker. Встреча от 08.12.2024

Запись нашей сегодняшней встречи -
YOUTUBE
RUTUBE

Спасибо всем кто пришел, за участие!💪

На сегодняшней встрече с подписчиками, мы на примере, разобрали:
— что такое архивы JAR, WAR и EAR и для чего они нужны.
— как создать и запускать приложения без IDea.
— как организовать сборку через основные сборщики Maven и Gradle.
— как создать простейшее веб-приложение и задеплоить его на сервер в Docker контейнер.

Смотрите, комментируйте, задавайте вопросы! Обязательно подписывайтесь на ютуб и рутюб каналы!!!

Всем хорошего настроения! 🫡✌️
Транзакционный менеджмент с помощью AOP

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


1. Транзакционный менеджмент: Основы

Транзакция — это единица работы, которая либо выполняется полностью, либо откатывается при возникновении ошибки. Примером транзакции может быть:
Добавление пользователя в базу данных.
Перевод денег между счетами.


Роль AOP в транзакционном менеджменте


Spring AOP позволяет автоматически обрабатывать транзакции:
Начинать транзакцию перед выполнением метода.
Фиксировать транзакцию после успешного выполнения.
Откатывать транзакцию при возникновении исключения.


Аннотация @Transactional

В Spring для работы с транзакциями используется аннотация @Transactional.

2. Пример использования транзакционного менеджмента

Шаг 1: Добавление зависимостей
Добавьте в pom.xml зависимости для Spring Data JPA и базы данных:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>


Шаг 2: Настройка базы данных
Добавьте настройки H2 в application.properties:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect


Шаг 3: Бизнес-логика с транзакциями


Создайте сущность:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private double balance;

// Getters and Setters
}


Репозиторий для работы с данными:
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {}


Сервис с транзакционным методом:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Transactional
public void transferMoney(Long fromUserId, Long toUserId, double amount) {
User fromUser = userRepository.findById(fromUserId)
.orElseThrow(() -> new RuntimeException("User not found"));
User toUser = userRepository.findById(toUserId)
.orElseThrow(() -> new RuntimeException("User not found"));

fromUser.setBalance(fromUser.getBalance() - amount);
toUser.setBalance(toUser.getBalance() + amount);

userRepository.save(fromUser);
userRepository.save(toUser);

if (amount > 1000) {
throw new RuntimeException("Transfer limit exceeded!");
}
}
}


Шаг 4: Проверка

Создайте тестовый контроллер:
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/transfer")
public void transferMoney(@RequestParam Long fromUserId, @RequestParam Long toUserId, @RequestParam double amount) {
userService.transferMoney(fromUserId, toUserId, amount);
}
}
Теперь, если перевести более 1000 единиц, метод выбросит исключение, а все изменения в базе данных будут откатаны.


#Java #Training #Spring #AOP #AOP_Transaction
Что выведет код?

public class Task091224_1 {
public static void main(String[] args) {
Random random = new Random(10);
System.out.println(random.nextInt(100));

random.setSeed(10);
System.out.println(random.nextInt(100));

Random anotherRandom = new Random(10);
System.out.println(anotherRandom.nextInt(100));

random.setSeed(20);
System.out.println(random.nextInt(100));
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
38%
13 13 13 53
25%
23 54 23 87
25%
54 54 55 87
13%
23 23 54 64
Постарайся вообще прийти...🤪😂

https://t.me/Java_for_beginner_dev

#Mems
Все аннотации AOP и их использование

1. @Aspect

Обозначает класс как аспект.
@Aspect
@Component
public class LoggingAspect {
// Pointcut и Advice
}


2. @Pointcut

Определяет срез, на который будет ссылаться Advice.
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}


3. @Before

Совет, выполняющийся перед методом.
@Before("serviceLayer()")
public void logBefore() {
System.out.println("Before method");
}


4. @After

Совет, выполняющийся после метода, независимо от его результата.

@After("serviceLayer()")
public void logAfter() {
System.out.println("After method");
}


5. @AfterReturning

Совет, выполняющийся после успешного выполнения метода.
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Method returned: " + result);
}


6. @AfterThrowing

Совет, выполняющийся при выбросе исключения.
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowing(Exception ex) {
System.out.println("Exception thrown: " + ex.getMessage());
}


7. @Around

Оборачивает метод, позволяя контролировать его выполнение.

@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before method");
Object result = joinPoint.proceed();
System.out.println("After method");
return result;
}


8.
@EnableAspectJAutoProxy

Включает поддержку AOP в приложении.
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}


#Java #Training #Spring #AOP #AOP_Annotations
Введение в Spring Security

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

1. Что такое Spring Security?


Spring Security решает две основные задачи:
Аутентификация — процесс проверки подлинности пользователя.
Авторизация — проверка прав пользователя для выполнения определённых действий.


Ключевые возможности:
Аутентификация и авторизация.
Интеграция с популярными механизмами безопасности (LDAP, OAuth2, JWT).


Защита от атак:
CSRF (Cross-Site Request Forgery).
XSS (Cross-Site Scripting).
Clickjacking.
Шифрование паролей.
Конфигурируемая модель безопасности с возможностью расширения.


2. Основные компоненты Spring Security

2.1 SecurityFilterChain
Основной компонент, который перехватывает HTTP-запросы и применяет к ним правила безопасности.

2.2 AuthenticationManager
Управляет процессом аутентификации. Это центральная точка, которая определяет, прошёл ли пользователь проверку подлинности.

2.3 UserDetailsService
Интерфейс, отвечающий за получение данных пользователя (например, имени и пароля) из базы данных.

2.4 PasswordEncoder
Интерфейс для шифрования и проверки паролей. Самый популярный его реализация — BCryptPasswordEncoder.

2.5 GrantedAuthority
Представляет права пользователя (например, роли).

3. Установка и настройка Spring Security

Шаг 1: Подключение зависимостей
Для работы с Spring Security добавьте зависимости в pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>


Шаг 2: Настройка безопасности
Создайте класс конфигурации:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.formLogin()
.and()
.logout();
return http.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}


Шаг 3: Настройка пользователей

Создайте пользователей в памяти для тестирования:
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class InMemoryUserDetailsService {

@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();

UserDetails admin = User.withUsername("admin")
.password(passwordEncoder.encode("admin"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user, admin);
}
}


4. Проверка безопасности

Запустите приложение и откройте в браузере:
/user — доступ только для пользователя с ролью USER или ADMIN.
/admin — доступ только для пользователя с ролью ADMIN.


#Java #Training #Spring #Security
Что выведет код?

import java.util.TreeSet;

public class Task101224_1 {
public static void main(String[] args) {
TreeSet<String> set = new TreeSet<>((s1, s2) -> s2.length() - s1.length());

set.add("one");
set.add("three");
set.add("four");
set.add("two");
set.add("five");

System.out.println(set);
}
}


#Tasks
Лучше б она ему биткоины купить предложила...🧐🤪

https://t.me/Java_for_beginner_dev

#Mems
В рамках дополнительной информации, устраняем пробел в знаниях по настройке поведения JVM.

Управление JVM (Java Virtual Machine) командами

Управление JVM (Java Virtual Machine) командами — это процесс конфигурации работы JVM через параметры, которые передаются при её запуске. Эти параметры позволяют настраивать поведение JVM, включая управление памятью, настройку сборщика мусора, логирование, отладку и безопасность. Все параметры JVM можно разделить на несколько категорий.

1. Основные типы параметров JVM

1.1. Стандартные параметры
Эти параметры поддерживаются всеми JVM и не зависят от конкретной реализации.

Например:
-cp или -classpath — указывает путь к классам и JAR-файлам.
-Dproperty=value — установка пользовательских системных свойств.
-version — выводит версию JVM.
-help — показывает справку по основным параметрам JVM.


Пример:
java -cp myapp.jar -Dconfig.file=app.properties com.example.Main


1.2. Параметры для настройки памяти

Эти параметры позволяют управлять распределением памяти в JVM:
-Xms<size> — начальный размер кучи (heap).
-Xmx<size> — максимальный размер кучи.
-Xss<size> — размер стека для каждого потока.
-XX:MetaspaceSize=<size> — начальный размер метапространства (метаданных классов).
-XX:MaxMetaspaceSize=<size> — максимальный размер метапространства.


Пример:
java -Xms512m -Xmx2g MyApp


1.3. Расширенные параметры (-X и -XX)

Эти параметры специфичны для реализации JVM (обычно HotSpot) и предоставляют дополнительные возможности:
-X параметры: упрощённые параметры, поддерживаемые большинством JVM.
-Xdebug — включает отладку.
-Xloggc:<file> — логирование работы сборщика мусора в файл.
-XX параметры: детальные настройки и эксперименты.
-XX:+UseG1GC — использование сборщика мусора G1.
-XX:ParallelGCThreads=<n> — количество потоков для параллельной сборки мусора.
-XX:+PrintGCDetails — выводит информацию о работе GC.
-XX:+HeapDumpOnOutOfMemoryError — создаёт дамп памяти при переполнении.


Пример:
java -XX:+UseG1GC -XX:+PrintGCDetails -XX:MaxHeapFreeRatio=70 MyApp


1.4. Логирование и диагностика

Эти параметры помогают анализировать производительность и поведение JVM:
-verbose:class — выводит информацию о загрузке классов.
-verbose:gc — выводит информацию о работе сборщика мусора.
-Xlog — гибкое управление логированием (начиная с Java 9).
Пример: -Xlog:gc* для логирования всех событий, связанных с GC.
-XX:+UnlockDiagnosticVMOptions — включает диагностические параметры.
Например: -XX:+PrintCompilation — выводит информацию о компиляции JIT.


Пример:
java -Xlog:gc* MyApp


1.5. Параметры безопасности
-Djava.security.manager — включает менеджер безопасности.
-Djava.security.policy=<file> — указывает путь к файлу политики безопасности.


2. Распределение памяти и работа GC

2.1. Память JVM

JVM делит память на несколько областей:
Heap (куча): для объектов.
Metaspace: для метаданных классов (начиная с Java 8).
Stack: для хранения вызовов методов и локальных переменных.
Native Memory: для работы JVM вне управляемой памяти.


2.2. Управление сборщиком мусора (GC)

Выбор GC сильно влияет на производительность:
-XX:+UseSerialGC — однопоточный сборщик мусора.
-XX:+UseParallelGC — многопоточный, ориентирован на максимальную пропускную способность.
-XX:+UseG1GC — G1 (Garbage-First) для больших приложений.
-XX:+UseZGC — Z Garbage Collector для низкой задержки (Java 11+).
-XX:+UseShenandoahGC — Shenandoah для низкой задержки (Java 12+).


3. Управление через команды операционной системы


Помимо параметров JVM, можно использовать команды ОС для управления процессом:
jps — список запущенных JVM.
jstat — статистика памяти и GC.
jmap — информация о памяти (heap dump).
jstack — вывод стека потоков.
jcmd — универсальный инструмент управления JVM.


4. Советы и рекомендации

Используйте флаги -Xlog вместо устаревших -verbose:gc в современных версиях Java.
Настройка памяти (-Xms и -Xmx) должна учитывать нагрузку приложения.
Анализируйте дампы памяти для отладки утечек.
Включайте диагностику (-XX:+UnlockDiagnosticVMOptions) только для тестирования.


#Java #Training #Medium #JVM_Commands
Основные принципы безопасности

Безопасность приложения должна обеспечиваться на всех уровнях — от архитектуры до шифрования данных.

1. Принципы обеспечения безопасности

1.1 Принцип наименьших привилегий
Каждый пользователь или система должны иметь минимальный набор разрешений, необходимый для выполнения своих задач.
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
);


1.2 Защита от атак CSRF
Spring Security по умолчанию включает защиту от CSRF.
CSRF — это атака, когда злоумышленник подделывает запрос от имени пользователя.

Можно настроить исключение для REST API:
http.csrf().disable();


1.3 Шифрование паролей
Пароли всегда должны храниться в зашифрованном виде. Используйте BCryptPasswordEncoder:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}


1.4 Аудит и логирование
Всегда логируйте попытки доступа и события безопасности:
http.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin(form -> form
.successHandler((req, res, auth) -> System.out.println("Login success: " + auth.getName()))
.failureHandler((req, res, ex) -> System.out.println("Login failure: " + ex.getMessage()))
);


2. Шифрование данных

Используйте HTTPS для передачи данных между клиентом и сервером. Это защищает от перехвата данных в сети.

3. Проверка ввода и защита от XSS

Валидируйте входные данные и используйте механизмы защиты от XSS:
Применяйте фильтрацию данных.
Используйте HTML-энкодинг:

String safeContent = HtmlUtils.htmlEscape(unsafeContent);


Пример безопасного REST API с аутентификацией

Создадим REST API, который защищён с помощью JWT.

Шаг 1: Подключение зависимости:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>


Шаг 2: Генерация JWT-токена:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;

public class JwtUtil {

private static final String SECRET_KEY = "mySecretKey";

public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 час
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}

public static boolean validateToken(String token, String username) {
String extractedUsername = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
return username.equals(extractedUsername);
}
}


Шаг 3: Применение токена в безопасности:
@Configuration
public class JwtSecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.addFilter(new JwtAuthenticationFilter())
.sessionManagement().disable();
return http.build();
}
}


#Java #Training #Spring #Security #SecurityPrincipies
Spring Security и его интеграция в проект

Spring Security — это мощный и гибкий фреймворк для обеспечения безопасности приложений на базе Spring. Он предоставляет возможности для аутентификации, авторизации, защиты от атак CSRF, управления сессиями, защиты REST API и многого другого.

Шаг 1: Добавление Spring Security в проект

Для начала необходимо добавить зависимости Spring Security в ваш проект. В зависимости от того, используете ли вы Maven или Gradle, это можно сделать следующим образом:

Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>


Gradle:
implementation 'org.springframework.boot:spring-boot-starter-security'


Шаг 2: Настройка Spring Security

Основные классы для настройки безопасности:
SecurityConfig — класс для настройки конфигурации безопасности.
UserDetailsService — сервис для работы с пользователями.
PasswordEncoder — компонент для кодирования паролей.


1. Конфигурация безопасности (Java-based)
Для настройки Spring Security создадим класс SecurityConfig, который будет аннотирован @Configuration и @EnableWebSecurity. Это обеспечит управление конфигурацией безопасности через Java-код.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // Отключение CSRF для упрощения тестирования
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/public/**").permitAll() // Доступ для всех
.anyRequest().authenticated() // Остальные запросы требуют аутентификации
)
.formLogin((form) -> form
.loginPage("/login") // Кастомная страница логина
.permitAll()
)
.logout((logout) -> logout.permitAll());

return http.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // Кодирование паролей
}
}


2. Реализация UserDetailsService
UserDetailsService используется для загрузки пользователей из хранилища данных (например, базы данных).

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Collections;

@Service
public class CustomUserDetailsService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// Заглушка: обычно данные берутся из базы
if ("admin".equals(username)) {
return User.builder()
.username("admin")
.password("$2a$10$7b4/uWVqOKu8.cQeMEjCE.Oz7w9f5qL4uLcyuKkKJ7TzPByHOfy92") // Закодированный "password"
.roles("ADMIN")
.build();
} else {
throw new UsernameNotFoundException("Пользователь не найден");
}
}
}


#Java #Training #Spring #Security #AddSecurity
Пароль можно закодировать с помощью следующего кода:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordEncoderTest {
public static void main(String[] args) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String rawPassword = "password";
String encodedPassword = encoder.encode(rawPassword);
System.out.println(encodedPassword);
}
}


3. Кастомизация страницы логина
Для создания кастомной страницы логина добавьте следующий контроллер:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

@GetMapping("/login")
public String login() {
return "login"; // Вернет login.html
}
}


Создайте шаблон login.html:


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<h2>Login Page</h2>
<form th:action="@{/login}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username"/><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password"/><br>
<button type="submit">Login</button>
</form>
</body>
</html>


#Java #Training #Spring #Security #AddSecurity
Что выведет код?

class Animal1112 {
void sound() {
System.out.println("Animal makes a sound");
}
}

class Dog1112 extends Animal1112 {
@Override
void sound() {
System.out.println("Dog barks");
}

void fetch() {
System.out.println("Dog fetches a ball");
}
}

class Cat1112 extends Animal1112 {
@Override
void sound() {
System.out.println("Cat meows");
}
}

public class Task111224_1 {
public static void main(String[] args) {
Animal1112 animal1 = new Dog1112();
Animal1112 animal2 = new Cat1112();
animal1.sound();
animal2.sound();

if (animal1 instanceof Dog1112) {
((Dog1112) animal1).fetch();
}

if (animal2 instanceof Dog1112) {
((Dog1112) animal2).fetch();
} else {
System.out.println("Not a Dog");
}
}
}


#Tasks
Ну так это ж девопс😂

https://t.me/Java_for_beginner_dev

#Mems