java面向对象学习

java面向对象学习

封装

封装是面向对象编程中的一种重要概念,它指的是将数据和操作数据的方法封装在一个类中,并通过访问修饰符来控制对这些数据和方法的访问权限。
封装的目的是隐藏类的内部实现细节,只暴露必要的接口给外部使用,从而提高代码的安全性和可维护性。
下面是一个简单的例子,演示了如何使用封装来隐藏类的内部实现细节:

public class Person {
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        if < 0) {
            new IllegalArgumentException("年龄不能为负数");
        }
        this.age = age;
    }
}

在上面的例子中,Person类封装了两个私有属性name和age,通过公有的get和set方法来访问和修改这些属性。
使用封装的好处有:
隐藏实现细节:外部无法直接访问类的私有属性,只能通过公有方法来访问和修改,这样可以减少对类内部实现的依赖。
提高安全性:通过封装可以控制对属性的访问权限,只暴露必要的接口给外部使用,可以防止外部对属性的非法修改。
提高可维护性:封装可以将类的内部实现细节与外部接口分离,当内部实现发生变化时,只需要修改类的内部代码,而不会影响外部代码。
使用封装的注意事项:
尽量将属性声明为私有的,通过公有的get和set方法来访问和修改属性。
在set方法中进行参数校验,确保属性的合法性。
不要过度封装,只封装必要的属性和方法。
封装是面向对象编程的重要特性之一,它可以提高代码的安全性、可维护性和可复用性。通过合理地使用封装,可以将复杂的系统分解成多个独立的模块,每个模块都有清晰的接口和实现,从而提高代码的可读性和可维护性。

重载

在Java中,方法重载(Overloading)是指在同一个类中,可以有多个方法具有相同的名称但具有不同的参数列表。当调用一个重载方法时,编译器会根据传入的参数类型和数量来决定具体调用哪个方法。
方法重载的特点包括:
方法名相同;
参数列表不同,可以是参数类型不同、参数个数不同、参数顺序不同;
返回类型可以相同也可以不同。
以下是一个关于方法重载的示例代码:

public class OverloadingExample {
    
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public String add(String a, String b) {
        return a + b;
    }
    
    public static void main(String[] args) {
        OverloadingExample example = new OverloadingExample();
        
        int sum1 = example.add(1, 2);
        System.out.println("Sum of 1 and 2 is: " + sum1);
        
        double sum2 = example.add(1.5, 2.5);
        System.out.println("Sum of 1.5 and 2.5 is: " + sum2);
        
        String sum3 = example.add("Hello", " World");
        System.out.println("Concatenation of 'Hello' and ' World' is: " + sum3);
    }
}

在上述代码中,OverloadingExample类定义了三个名为add的方法,它们的参数列表分别为(int, int)、(double, double)和(String, String)。这三个方法的返回类型分别为int、double和String。
在main方法中,通过创建OverloadingExample对象,我们可以调用这三个重载方法并打印出结果。根据传入的参数类型和数量,编译器会自动选择合适的方法进行调用。在示例中,我们分别计算了两个整数的和、两个浮点数的和以及两个字符串的拼接结果。

继承

Java面向对象中的继承是指一个类可以继承另一个类的特性和行为。被继承的类称为父类或超类,继承的类称为子类或派生类。子类可以继承父类的字段、方法和内部类,并且可以添加自己的字段、方法和内部类。
下面是一个使用Java继承的示例代码:

// 定义一个父类Animal
class Animal {
    String name;
    int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println("Animal " + name + " is eating.");
    }
}

// 定义一个子类Dog,继承自Animal
class Dog extends Animal {
    String breed;

    public Dog(String name, int age, String breed) {
        super(name, age); // 调用父类的构造方法
        this.breed = breed;
    }

    public void bark() {
        System.out.println("Dog " + name + " is barking.");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建一个
        Animal animal = new Animal("Tom", 5);
        animal.eat();

        // 创建一个Dog对象
        Dog dog = new Dog("Max", 3, "Labrador");
        dog.eat(); // 继承自父类Animal的方法
        dog.bark(); // 子类自己的方法
    }
}

在上面的代码中,Animal是一个父类,它有一个字段name和一个方法eat。Dog是Animal的子类,它继承了父类的字段和方法,并且添加了一个自己的字段breed和方法bark。
在主函数中,我们创建了一个Animal对象和一个Dog对象,并调用它们的方法。Animal对象调用了继承自父类的eat方法,而Dog对象除了继承了eat方法,还调用了自己的bark方法。
继承使得代码可以更好地组织和重用,子类可以使用父类的代码,同时还可以添加自己的特定行为。

多态

Java面向对象的多态是指同一个方法在不同对象上具有不同的行为。多态性是面向对象编程的重要特性之一,它允许我们使用一个父类的引用来指向其子类的对象,从而实现对不同子类对象的统一处理。
下面简单的来说明多态的概念:

// 定义一个动物类
class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

// 定义一个狗类,继承自动物类
class Dog extends Animal {
    public void sound() {
        System.out.println("狗发出汪汪声");
    }
}

// 定义一个猫类,继承自动物类
class Cat extends Animal {
    public void sound() {
        System.out.println("猫发出喵喵声");
    }
}

public class PolymorphismExample {
    public static void main(String[] args) {
        Animal animal1 = new Dog(); // 使用父类的引用指向子类对象
        Animal animal2 = new Cat(); // 使用父类的引用指向子类对象

        animal1.sound(); // 调用的是子类Dog的sound方法
        animal2.sound(); // 调用的是子类Cat的sound方法
    }
}

在上面的例子中,Animal是一个父类,它有一个sound方法。Dog和Cat是Animal的子类,它们分别重写了sound方法。在main方法中,我们创建了一个Animal类型的引用animal1,并将其指向一个Dog对象;再创建了一个Animal类型的引用animal2,并将其指向一个Cat对象。当调用animal1和animal2的sound方法时,实际上会调用对应子类的sound方法,即输出不同的声音。
这就是多态的效果,通过使用父类的引用来调用子类的方法,实现了对不同子类对象的统一处理。

抽象类

Java抽象类是一种特殊的类,它不能被实例化,只能作为其他类的基类。抽象类用于定义一些共同的属性和行为,但无法完全实现,需要子类来继承并实现它们。
抽象类的特点如下:
抽象类使用abstract关键字来修饰,不能直接实例化。
抽象类可以包含抽象方法和非抽象方法。
抽象方法使用abstract关键字定义,没有方法体,只有方法声明。
子类继承抽象类时,必须实现所有的抽象方法,除非子类也是抽象类。
下面是一个抽象类的例子:

// 定义一个抽象类Animal
abstract class Animal {
    // 抽象方法,没有方法体
    public abstract void sound();

    // 非抽象方法
    public void sleep() {
        System.out.println("Animal is sleeping");
    }
}

// 继承抽象类Animal,实现抽象方法
class Cat extends Animal {
    public void sound() {
        System.out.println("Cat makes sound: Meow");
    }
}

// 继承抽象类Animal,实现抽象方法
class Dog extends Animal {
    public void sound() {
        System.out.println("Dog makes sound: Woof");
    }
}

public class Main {
    public static void main(String[] args) {
        // 无法实例化抽象类Animal
        // Animal animal = new Animal();

        // 创建Cat对象
        Cat cat = new Cat();
        cat.sound(); // 输出: Cat makes sound: Meow
        cat.sleep(); // 输出: Animal is sleeping

        // 创建Dog对象
        Dog dog = new Dog();
        dog.sound(); // 输出: Dog makes sound: Woof
        dog.sleep(); // 输出: Animal is sleeping
    }
}

在上面的例子中,Animal是一个抽象类,它定义了一个抽象方法sound()和一个非抽象方法sleep()。Cat和Dog是Animal的子类,它们必须实现抽象方法sound()。在main方法中,我们可以实例化Cat和Dog对象,并调用它们的方法。注意,我们无法直接实例化Animal对象,因为它是抽象的。

接口

Java接口是一种抽象数据类型,它定义了一组方法的规范,但没有具体的实现。接口可以被类实现,实现类需要提供接口中定义的所有方法的具体实现。接口可以用来实现多态性,使得不同的类可以通过实现相同的接口来实现相同的功能。
接口的定义使用关键字interface,接口中的方法默认是public abstract的,变量默认是public static final的。
下面是一个简单的接口示例代码:

public interface Animal {
    void eat();
    void sleep();
}

在上面的代码中,Animal接口定义了两个方法eat()和sleep(),这两个方法没有具体的实现。
接口的实现使用关键字implements,实现类需要提供接口中定义的所有方法的具体实现。下面是一个实现了Animal接口的类的示例代码:

public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating.");
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping.");
    }
}

在上面的代码中,Dog类实现了Animal接口,并提供了eat()和sleep()方法的具体实现。
使用接口的好处是可以实现多态性。下面是一个使用接口的示例代码:

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();
        animal.sleep();
    }
}

在上面的代码中,Main类创建了一个Dog对象,并将其赋值给Animal类型的变量animal。通过接口类型的引用,可以调用接口中定义的方法。在这个例子中,调用animal.eat()和animal.sleep()时实际上调用的是Dog类中的具体实现。
总结:Java接口是一种定义了一组方法规范的抽象数据类型,没有具体的实现。类可以实现接口并提供接口中定义的方法的具体实现。使用接口可以实现多态性,提高代码的灵活性和可扩展性。

泛型

Java 泛型是一种在编译时进行类型检查和类型安全的机制,它允许我们在类、接口、方法的定义中使用一个或多个类型参数,这些类型参数在使用时可以被替换为实际的类型。
泛型的优点:
提高代码的重用性和可读性,减少类型转换的错误。
在编译时进行类型检查,减少运行时出现的类型错误。
可以创建通用的算法和数据结构,使其适用于不同的数据类型。
下面是一个使用泛型的示例代码:

public class Box<T> {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }

    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setContent("Hello");
        String content = stringBox.getContent();
        System.out.println(content);

        Box<Integer> integerBox = new Box<>();
        integerBox.setContent(123);
        int number = integerBox.getContent();
        System.out.println(number);
    }
}

在上面的代码中,我们定义了一个泛型类 Box,其中 T 是类型参数。通过使用 T,我们可以在类中定义一个成员变量 content,它的类型是 T。我们还可以在类中定义使用 T 类型的方法。
在 main 方法中,我们创建了两个 Box 对象,一个是 Box 类型的 stringBox,另一个是 Box 类型的 integerBox。我们分别将字符串和整数类型的值设置到 content 成员变量中,并通过调用 getContent 方法获取到这些值。
通过使用泛型,我们可以在编译时就检查类型的正确性,并且不需要进行类型转换。这样可以提高代码的可读性和安全性。

枚举

Java中的枚举是一种特殊的数据类型,用于定义一组有限的常量。枚举常量在声明时被定义,并且不能被修改。枚举可以包含方法和构造函数。
以下是一个简单的枚举示例代码:

public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, S;
}

上面的,我们定义了一个名为DayOfWeek的枚举类型,并列出了一周中的所有天。这些枚举常量是不可修改的。
我们可以使用枚举类型的常量来声明变量,如下所示:

DayOfWeek today = DayOfWeek.MONDAY;

我们还可以为枚举类型添加方法,如下所示:

public enum DayOfWeek {
    MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日");

    private String chineseName;

    private DayOfWeek(String chineseName) {
        this.chineseName = chineseName;
    }

    public String getChineseName() {
        return chineseName;
    }
}

在上面的代码中,我们为枚举常量添加了一个名为chineseName的成员变量,并在构造函数中进行初始化。我们还添加了一个名为getChineseName()的方法,用于获取枚举常量的中文名称。
我们可以使用如下方式获取枚举常量的中文名称:

DayOfWeek today = DayOfWeek.MONDAY;
System.out.println(today.getChineseName());  // 输出:星期一

总结:Java中的枚举是一种特殊的数据类型,用于定义一组有限的常量。枚举常量在声明时被定义,并且不能被修改。枚举可以包含方法和构造函数,可以用于表示一组相关的常量。

注解

Java 注解(Annotation)是一种用于在程序中添加元数据的方式。它可以在类、方法、变量、参数等程序元素上添加额外的信息,这些信息可以在编译时被读取和处理。
Java 注解的使用方式是在注解名称前加上 @ 符号,放置在需要添加元数据的程序元素的上方。例如:

@AnnotationName
public class MyClass {
    @AnnotationName
    private int myField;

    @AnnotationName
    public void myMethod() {
        // 方法体
    }
}

Java 注解可以用于很多不同的场景,例如:
提供给编译器额外的信息,以便进行更严格的类型检查或者生成更高效的代码。
在运行时通过反射机制读取注解信息,用于实现自定义的逻辑。
自动生成文档或者生成代码。
Java 注解的定义是通过创建一个注解接口来实现的。注解接口使用 @interface 关键字进行定义,并可以添加成员变量和默认值。例如:

public @interface AnnotationName {
    String value() default "";
    int count() default 0;
}

上述代码定义了一个名为 AnnotationName 的注解接口,它包含了两个成员变量 value 和 count,并且都有默认值。
使用注解时,可以对成员变量进行赋值,也可以使用默认值。例如:

@AnnotationName(value = "Hello", count = 5)
public class MyClass {
    // 类体
}

上述代码使用了 AnnotationName 注解,并为 value 和 count 成员变量赋予了特定的值。
在编写代码时,可以通过反射机制读取注解信息,并根据注解的值执行相应的逻辑。例如:

Class<?> clazz = MyClass.class;
AnnotationName annotation = clazz.getAnnotation(AnnotationName.class);
if (annotation != null) {
    String value = annotation.value();
    int count = annotation.count();
    // 执行逻辑
}

上述代码通过反射机制获取 MyClass 类上的 AnnotationName 注解,并读取了其中的 value 和 count 值。
总结起来,Java 注解是一种用于在程序中添加元数据的方式,可以在类、方法、变量、参数等程序元素上添加额外的信息。通过定义注解接口和使用注解,可以实现更丰富的编程逻辑和功能。

异常处理

Java异常处理是一种机制,用于在程序运行过程中处理可能出现的错误或异常情况。异常处理可以帮助程序员识别和处理错误,使程序更加健壮和稳定。
Java中的异常分为两种类型:受检异常(Checked Exception)和非受检异常(Unchecked Exception)。受检异常是指在编译时需要进行处理的异常,如果不处理,编译器会报错。非受检异常是指在运行时可能出现的异常,不需要强制处理,但可以选择进行处理。
Java异常处理的关键字是try-catch块。try块中包含可能会抛出异常的代码,catch块用于捕获并处理异常。当try块中的代码抛出异常时,程序会跳转到对应的catch块,并执行catch块中的代码。
以下是一个简单的示例代码,演示了Java异常处理的基本用法:

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    public static int divide(int a, int b) {
        return a / b;
    }
}

在上述代码中,我们定义了一个divide方法,用于进行除法运算。在main方法中,我们调用divide方法,并传入参数10和0,这会导致除以零的异常(ArithmeticException)。
为了捕获这个异常,我们使用了try-catch块。在try块中,我们调用divide方法,并将结果赋值给result变量。如果divide方法抛出异常,程序会跳转到catch块,并执行其中的代码。在catch块中,我们打印出错误信息。
运行上述代码,输出结果为:Error: / by zero。这表明程序成功捕获了异常,并进行了相应的处理。
除了try-catch块,Java还提供了finally块。finally块中的代码无论是否发生异常都会执行。它通常用于释放资源或进行清理操作。以下是一个示例代码:

public class FinallyExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            System.out.println("Finally block");
        }
    }

    public static int divide(int a, int b) {
        try {
            return a / b;
        } catch (ArithmeticException e) {
            throw e;
        } finally {
            System.out.println("Inner finally block");
        }
    }
}

在上述代码中,我们在divide方法中添加了一个内部的finally块。无论是否发生异常,这个finally块中的代码都会执行。在main方法中,我们调用divide方法,并在catch块中打印出错误信息。无论是否发生异常,finally块中的代码都会执行。
运行上述代码,输出结果为:
Error: / by zero
Inner finally block
Finally block
这表明程序成功捕获了异常,并执行了finally块中的代码。
总结:
Java异常处理通过try-catch块来捕获和处理异常。try块中包含可能抛出异常的代码,catch块用于捕获并处理异常。finally块中的代码无论是否发生异常都会执行。异常处理有助于提高程序的健壮性和稳定性。

多线程

Java多线程是指在一个程序中同时运行多个线程,每个线程都是独立执行的,可以并发执行或者并行执行。多线程可以提高程序的效率和响应速度,特别适用于需要处理大量并发任务的场景。
在Java中,可以通过两种方式创建多线程:继承Thread类和实现Runnable接口。
继承Thread类:

public class MyThread extends Thread {
    @Override
    public void run() {
        // 线程执行的代码逻辑
        for (int i = 0; i < 10; i++) {
            System.out.println("Thread: " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 启动线程
        // 主线程执行的代码逻辑
        for (int i = 0; i < 10; i++) {
            System.out.println("Main: " + i);
        }
    }
}

实现Runnable接口:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程执行的代码逻辑
        for (int i = 0; i < 10; i++) {
            System.out.println("Thread: " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start(); // 启动线程
        // 主线程执行的代码逻辑
        for (int i = 0; i < 10; i++) {
            System.out.println("Main: " + i);
        }
    }
}

无论是继承Thread类还是实现Runnable接口,都需要重写run()方法,在run()方法中编写线程要执行的代码逻辑。然后通过创建线程对象并调用start()方法来启动线程。
以上代码中,主线程和子线程交替执行,输出结果可能是乱序的。这是因为多线程的执行顺序是不确定的,取决于线程调度器的调度策略。
需要注意的是,多线程编程需要考虑线程安全问题,例如共享资源的并发访问问题,可以使用synchronized关键字或者Lock对象来实现线程同步。

IO流

Java IO流是Java中用于处理输入输出操作的一种机制。它提供了一种统一的方式来处理不同类型的输入输出,包括文件、网络、内存等。
Java IO流主要分为字节流和字符流两种类型。字节流主要用于处理二进制数据,而字符流则用于处理字符数据。每种类型又分为输入流和输出流。
下面是一些常见的Java IO流的例子代码:
字节流的文件读取和写入:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        try {
            // 读取文件
            FileInputStream fis = new FileInputStream("input.txt");
            int data;
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
            fis.close();

            // 写入文件
            FileOutputStream fos = new FileOutputStream("output.txt");
            String text = "Hello, World!";
            fos.write(text.getBytes());
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符流的文件读取和写入:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharacterStreamExample {
    public static void main(String[] args) {
        try {
            // 读取文件
            FileReader reader = new FileReader("input.txt");
            int data;
            while ((data = reader.read()) != -1) {
                System.out.print((char) data);
            }
            reader.close();

            // 写入文件
            FileWriter writer = new FileWriter("output.txt");
            String text = "Hello, World!";
            writer.write(text);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

缓冲流的使用:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedStreamExample {
    public static void main(String[] args) {
        try {
            // 读取文件
            BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();

            // 写入文件
            BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"));
            String text = "Hello, World!";
            writer.write(text);
            writer.newLine();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这些例子展示了如何使用Java IO流来进行文件读写操作。除了文件操作,Java IO流还可以用于处理网络通信、内存数据等。根据具体的需求,可以选择适合的输入输出流来进行操作。

集合

Java集合是一种用于存储和操作多个对象的数据结构。它提供了一组接口和类,可以方便地操作和管理数据。
Java集合框架包括以下主要接口:
List:有序的集合,可以包含重复元素。常用实现类有ArrayList和LinkedList。
Set:无序的集合,不允许重复元素。常用实现类有HashSet和TreeSet。
Map:键值对的集合,每个键最多只能映射到一个值。常用实现类有HashMap和TreeMap。
Queue:队列,先进先出的数据结构。常用实现类有LinkedList和PriorityQueue。
下面是一些Java集合的示例代码:
List示例代码:

List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");
System.out.println(list);  // 输出:[apple, banana, orange]

Set示例代码:

Set<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("orange");
System.out.println(set);  // 输出:[apple, banana, orange]

Map示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
System.out.println(map);  // 输出:{apple=1, banana=2, orange=3}

Queue示例代码:

Queue<String> queue = new LinkedList<>();
queue.offer("apple");
queue.offer("banana");
queue.offer("orange");
System.out.println(queue);  // 输出:[apple, banana, orange]
posted @ 2023-10-12 16:23  BattleofZhongDinghe  阅读(12)  评论(0编辑  收藏  举报