[Studying Java Core] #6

Inwook Baek ·2021년 10월 6일
0

Java Study

목록 보기
6/6
post-thumbnail

Objects

A typical object-oriented program consists of a set of interacting objects. Each object has its own state separated from others. Each object is an instance of a particular class (type) that defines common properties and possible behavior for its objects.

All classes from the standard library (String, Date) and classes defined by programmers are reference types which means that variables of these types store addresses where the actual objects are located. In this regard, the comparison and assignment operations work with objects differently than with primitive types.

There is an important concept in programming called immutability. Immutability means that an object always stores the same values. If we need to modify these values, we should create a new object. The common example is the standard String class. Strings are immutable objects so all string operations produce a new string. Immutable types allow you to write programs with fewer errors.

Example 1:
There is a class Person that has two fields: name and age. Your task is to implement the method changeIdentities. It should swap all information (age and name) between two persons p1 and p2.

It is known that objects p1 and p2 can't be null.

class Person {
    String name;
    int age;
}

class MakingChanges {
    public static void changeIdentities(Person p1, Person p2) {
        String tempName = p1.name;
       int tempAge = p1.age;
       
       p1.name = p2.name;
       p1.age = p2.age;
       p2.name = tempName;
       p2.age = tempAge; 

    }
}

Example 2:
You decide to recall the happy days of your childhood and play Heroes. Of course, you need the army.

Your task is to create objects: 5 Unit, 3 Knight, 1 General, 1 Doctor.

Don't forget to give them names!

class Army {

    public static void createArmy() {
        new Unit("U1");
        new Unit("U2");
        new Unit("U3");
        new Unit("U4");
        new Unit("U5");
        new Knight("K1");
        new Knight("K2");
        new Knight("K3");
        new General("G1");
        new Doctor("D1");
        
    }

    static class Unit {
        static String nameUnit;
        static int countUnit;

        public Unit(String name) {
            countUnit++;
            nameUnit = name;

        }
    }

    static class Knight {
        static String nameKnight;
        static int countKnight;

        public Knight(String name) {
            countKnight++;
            nameKnight = name;

        }
    }

    static class General {
        static String nameGeneral;
        static int countGeneral;

        public General(String name) {
            countGeneral++;
            nameGeneral = name;

        }
    }

    static class Doctor {
        static String nameDoctor;
        static int countDoctor;

        public Doctor(String name) {
            countDoctor++;
            nameDoctor = name;

        }
    }

    public static void main(String[] args) {
        createArmy();
        System.out.println(Unit.countUnit);
        System.out.println(Knight.countKnight);
        System.out.println(General.countGeneral);
        System.out.println(Doctor.countDoctor);
    }

}

Interface

The general idea of OOP and one of its principles is abstraction. It means that real-world objects can be represented by their abstract models. Designing models is about focusing on the essential features of the objects and discarding the others. To understand what it means, let's take a look at a pencil. A pencil is an object that we can use to draw. Other properties such as material or length may be important to us sometimes but do not define the idea of a pencil.

Imagine we need to create a graphical editor program. One of the basic functions of the program is drawing. Before drawing, the program asks a user to select a drawing tool. It can be a pen, pencil, brush, highlighter, marker, spray, and others. Each tool from a set has its own specific features: a pencil and a spray leave different marks and that matters. But there is also an essential feature that unites them: the ability to draw.

Now let's consider the Pencil class, which is an abstraction of a pencil. As we already discussed the class at least should have the draw method that accepts a model of a curve. This is a crucial function of a pencil for our program. Suppose Curve is a class that represents some curve:

class Pencil {
    ...
    public void draw(Curve curve) {...}
}

Let's define classes for other tools, for example, a brush:

class Brush {
    ...
    public void draw(Curve curve) {...}
}

Each of them has the method draw, although uses it in its own fashion. The ability to draw is a common feature for all of them. Let's call this feature DrawingTool. Then we can say that if a class has the DrawingTool feature, then it should be able to draw, that means the class should have the void draw(Curve curve) {...} method.

Java allows declaring this feature by introducing interfaces. This is how our interface looks like:

interface DrawingTool {
    void draw(Curve curve);
}

It declares the draw method without implementation.

Now let's mark classes that are able to draw by adding implements DrawingTool to the class declaration. If a class implements an interface, it has to implement all declared methods:

class Pencil implements DrawingTool {
    ...
    public void draw(Curve curve) {...}
}

class Brush implements DrawingTool {
    ...
    public void draw(Curve curve) {...}
}

Now just a quick look at the class declaration is enough to understand that class is able to draw. In other words, the main idea of an interface is declaring functionality.

Another important profit of introducing interfaces is that you can use them as a type:

DrawingTool pencil = new Pencil();
DrawingTool brush = new Brush();

Now both a pencil and a brush objects have the same type. It means that both classes can be treated in a similar way as a DrawingTool. This is another way of supporting polymorphism, which helps to design reusable drawing function of the graphical editor program.

An interface can't contain fields (only constants), constructors, or non-public abstract methods. Let's declare an interface containing all possible members:

interface Interface {
        
    int INT_CONSTANT = 0; // it's a constant, the same as public static final int INT_FIELD = 0
        
    void instanceMethod1();
        
    void instanceMethod2();
        
    static void staticMethod() {
        System.out.println("Interface: static method");
    }
        
    default void defaultMethod() {
        System.out.println("Interface: default method. It can be overridden");
    }

    private void privateMethod() {
        System.out.println("Interface: private methods in interfaces are acceptable but should have a body");
    }
}

Examlpe 1:
The Circle class represents a circle. Implement the Measurable interface and add a single method area that returns the area of a circle.

Note: Java has a built-in constant for PI: Math.PI

The class will be tested by creating an instance of Circle and invoking its area method:

Measurable circle = new Circle(1);
double area = circle.area(); // 3.14...

Solution

class Circle implements Measurable {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override 
    public double area() {
        return Math.PI * radius * radius; 
    }
    
    
}

// do not change the interface
interface Measurable {
    double area();
}

Examlpe 2:
Suppose, you are writing a geometric engine. Now it includes classes Circle, Rectangle and interfaces Movable and Scalable.

You need:

  1. to write a new interface MutableShape that extends both existing interfaces;
  2. to implement the new interface in each class;
  3. to override methods move and scale in both classes:
  4. scale should multiply the radius of a circle by the specified factor;
  5. scale should multiply width and height of a rectangle by the specified factor;
  6. move should add dx and dy to the center of a circle;
  7. move should add dx and dy to the upper-left corner of a rectangle.
  8. See the provided code and read comments to understand your task better. Now the code is not compiled.

Note:

do not remove existing classes and their members (including getters and constructors).
do not make your classes and interfaces public
After your changes, the following code should be compiled:

MutableShape circle = new Circle(2.0f, 3.5f, 10.1f);

circle.move(10.1f, 20.2f);
circle.scale(2.2f);

((Circle) circle).getRadius();

Solution

interface Movable {

    void move(float dx, float dy);
}

interface Scalable {

    void scale(float factor);
}

interface MutableShape extends Movable, Scalable {

}

final class Circle implements MutableShape {

    /**
     * Defines the horizontal position of the center of the circle
     */
    private float centerX;

    /**
     * Defines the vertical position of the center of the circle
     */
    private float centerY;

    /**
     * Defines the radius of the circle
     */
    private float radius;

    public Circle(float centerX, float centerY, float radius) {
        this.centerX = centerX;
        this.centerY = centerY;
        this.radius = radius;
    }

    public float getCenterX() {
        return centerX;
    }

    public float getCenterY() {
        return centerY;
    }

    public float getRadius() {
        return radius;
    }

    @Override
    public void move(float dx, float dy) {
        centerX += dx;
        centerY += dy;
    }

    @Override
    public void scale(float factor) {
        radius *= factor;
    }
}

final class Rectangle implements MutableShape {

    /**
     * Defines the X coordinate of the upper-left corner of the rectangle.
     */
    private float x;

    /**
     * Defines the Y coordinate of the upper-left corner of the rectangle.
     */
    private float y;

    /**
     * Defines the width of the rectangle.
     */
    private float width;

    /**
     * Defines the height of the rectangle.
     */
    private float height;

    public Rectangle(float x, float y, float w, float h) {
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public float getWidth() {
        return width;
    }

    public float getHeight() {
        return height;
    }

    @Override
    public void move(float dx, float dy) {
        x += dx;
        y += dy;
    }

    @Override
    public void scale(float factor) {
        width *= factor;
        height *= factor;
    }
}

Examlpe 3:
Imagine an interface AccountService that is designed for keeping track of accounts and balances.

It has two abstract methods:
findAccountByOwnerId(long id) takes user id and returns this user account or null in case no account was found;
countAccountsWithBalanceGreaterThan(long balance) returns the number of accounts with a balance exceeding the provided number.

There are also two classes: Account and User, each of them having several fields. The owner field of the Account class is an object of the User type. You can find the details in the provided template.

Your task is to create AccountServiceImpl class that implements AccountService interface and its two methods.
It should have a constructor that takes an Account array which will be used for searching when either of the methods is called.

For example, to find an account by the user id we need to go through all the accounts from this array and compare these accounts owners id with the given id.

Here's an example of how these methods will be called:

Account[] accounts = ...
AccountService service = new AccountServiceImpl(accounts);
service.findAccountByOwnerId(10L); // returns an account where owner id is 10

Solution

interface AccountService {
    /**
     * It finds an account by owner id
     * @param id owner unique identifier
     * @return account or null
     */
    Account findAccountByOwnerId(long id);

    /**
     * It count the number of account with balance > the given value
     * @param value
     * @return the number of accounts
     */
    long countAccountsWithBalanceGreaterThan(long value);
}

class AccountServiceImpl implements AccountService {
    
    Account[] accounts;
    
    AccountServiceImpl(Account[] accounts) {
        this.accounts = accounts.clone();
    }

    @Override
    public Account findAccountByOwnerId(long id) {
        Account result = null;
        for (Account account : accounts) {
            if (account.getOwner().getId() == id) {
                result = account;
            }
        }
        return result;
    }

    @Override
    public long countAccountsWithBalanceGreaterThan(long value) {
        long count = 0;
        for (Account account : accounts) {
            if (account.getBalance() > value) {
                count++;
            }
        }
        return count;
    }
} 

class Account {

    private long id;
    private long balance;
    private User owner;

    public Account(long id, long balance, User owner) {
        this.id = id;
        this.balance = balance;
        this.owner = owner;
    }

    public long getId() { 
        return id; 
    }

    public long getBalance() { 
        return balance; 
    }

    public User getOwner() { 
        return owner; 
    }
}

class User {

    private long id;
    private String firstName;
    private String lastName;

    public User(long id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public long getId() { 
        return id; 
    }

    public String getFirstName() { 
        return firstName; 
    }

    public String getLastName() { 
        return lastName; 
    }
}

Comparable

Examlpe 1:
In the example below, you can see our class Article. It has two fields: title and size. You should implement comparing articles by their size, and if their sizes are equal, compare them by title.

Solution


class Article implements Comparable<Article> {
    private String title;
    private int size;

    public Article(String title, int size) {
        this.title = title;
        this.size = size;
    }

    public String getTitle() {
        return this.title;
    }

    public int getSize() {
        return this.size;
    }

    @Override
    public int compareTo(Article otherArticle) {
        int titleCompare = getTitle().compareTo(otherArticle.getTitle());
        int sizeCompare = Integer.compare(getSize(), otherArticle.getSize());
        
        if (sizeCompare == 0) {
            return titleCompare;
        }
        return sizeCompare;
    }
    
}

Input

How to bake an awesome cake?-300
Alice likes pancakes...But who doesn't?-800
Germany wants to win EURO 2020!-500

Output

How to bake an awesome cake? 300
Germany wants to win EURO 2020! 500
Alice likes pancakes...But who doesn't? 800

Examlpe 2:
For the example given in the topic about people, implement the compareTo method. It should compare people by name, and if they have the same name, compare them by age.

public class Person {
    private String name;
    private int age;
    private int height;
    private int weight;
    
    // constructor

    // getters, setters
}

Solution

class Person implements Comparable<Person> {
    private String name;
    private int age;
    private int height;
    private int weight;

    public Person(String name, int age, int height, int weight) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    public int getHeight() {
        return this.height;
    }

    public int getWeight() {
        return this.weight;
    }

    @Override
    public int compareTo(Person otherPerson) {
        int nameCompare = getName().compareTo(otherPerson.getName());
        int ageCompare = Integer.compare(getAge(), otherPerson.getAge());
        
        if (nameCompare == 0) {
            return ageCompare;
        }
        return nameCompare;
    }
}

Input

Tom-22-185-65
Bob-22-175-85
Kris-30-180-90

Output

Bob 22 175 85
Kris 30 180 90
Tom 22 185 65

Processing Strings

It's possible to convert between strings and character arrays using special methods like valueOf() and toCharArray().

char[] chars = { 'A', 'B', 'C', 'D', 'E', 'F' };

String stringFromChars = String.valueOf(chars); // "ABCDEF"

char[] charsFromString = stringFromChars.toCharArray(); 
// { 'A', 'B', 'C', 'D', 'E', 'F' }

String theSameString = new String(charsFromString); // "ABCDEF"

There is another way to turn a string into an array. Take a look:

String text = "Hello";
String[] parts = text.split(""); // {"H", "e", "l", "l", "o"}

A string can be divided into an array of strings based on a delimiter. To perform this, we call the split method, which divides the string into substrings by a separator. In the previous example, we used the "" delimiter, which automatically splits a string into smaller elements (substrings) that consist of one char.

If the delimiter is specified, the method returns an array of all the substrings. Note that the delimiter itself is not included in any of the substrings:

String sentence = "a long text";
String[] words = sentence.split(" "); // {"a", "long", "text"}

Let's try to split an American phone number into the country code, the area code, the central office code, and other remaining digits:

String number = "+1-213-345-6789";
String[] parts = number.split("-"); // {"+1", "213", "345", "6789"}

Iterating over a string

It's possible to iterate over the characters of a string using a loop (while, do-while, for-loop).

See the following example:

String scientistName = "Isaac Newton";

for (int i = 0; i < scientistName.length(); i++) {
    System.out.print(scientistName.charAt(i) + " ");
}

In strings, like in arrays, indexing begins from 0. In our example, the for-loop iterates over the string "Isaac Newton" . With each iteration, the charAt method returns the current character at the i index, and that character is then printed to the console, followed by a blank space.

Here is what the code outputs as a result:

I s a a c   N e w t o n 

Examlpe 1:
Write a program that reads a string and then output another string with doubled characters.

Solution

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class jetBrains_doubleCharacters {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String word = br.readLine();

        for (int i = 0; i < word.length(); i++) {
            System.out.print(word.charAt(i));
            System.out.print(word.charAt(i));
        }

    }
}

Input

The

Output

TThhee

Examlpe 2:
Write a program that reads a string, and then outputs the string without its middle character when the length is odd, and without the middle 2 characters when the length is even.

Solution

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class jetBrains_cuttingMiddle {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String word = br.readLine();
        StringBuilder sb = new StringBuilder(word);

        if (word.length() % 2 != 0) {
            sb.deleteCharAt(word.length() / 2);
        } else {
            sb.deleteCharAt(word.length() / 2);
            sb.deleteCharAt(word.length() / 2 - 1);
        }
        String resString = sb.toString();

        for (int i = 0; i < resString.length(); i++) {
            System.out.print(resString.charAt(i));
        }

    }
}

Input

Hello

Output

Helo

Examlpe 3:
You want to hack a website now. First, get all the available parameters that you can find in the URL. Then print them in the "key : value" format. If a parameter doesn't have value, print "not found".

If you find the password (parameter pass), you should print its value after all parameters once again, but with a key password. If a URL does not contain parameter pass, do not print anything after the listed parameters. However, if pass parameter is present, its value cannot be empty.

Note: the order of parameters should be the same as in the URL.
Advice: Check examples for better understanding and carefully learn the structure of the URL.

Solution

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class jetBrains_parseUrl {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String url = br.readLine();
        StringBuilder pass = new StringBuilder();

        String data = url.substring(url.indexOf("?") + 1, url.length());
        
        String[] keys = data.split("&");

        for (int i = 0; i < keys.length; i++) {
            String s = keys[i];
            pass.append("pass".equals(s.substring(0, s.indexOf('='))) ? s.substring(s.indexOf('=') + 1) : "");
            System.out.print(s.substring(0, s.indexOf('=')) + " : ");
            System.out.println((s.indexOf('=') + 1 == s.length()) ? "not found" : s.substring(s.indexOf('=') + 1));
        }
        System.out.print(pass.length() > 0 ? "password : " + pass : "");
    }
}

Input

https://target.com/index.html?pass=12345&port=8080&cookie=&host=localhost

Output

pass : 12345
port : 8080
cookie : not found
host : localhost
password : 12345

Examlpe 3:
Write a program that finds the frequency of occurrences of a substring in a given string. Substrings cannot overlap: for example, the string ababa contains only one substring aba.

Solution

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class jetBrains_occur {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String word = br.readLine();
        String sub = br.readLine();
        
        Pattern pattern = Pattern.compile(sub);
        Matcher matcher = pattern.matcher(word);
        int count = 0;
        while (matcher.find()) {
            count++;
        }
        System.out.print(count);
    }
}

Input

hello yellow jello
ll

Output

3

0개의 댓글