三次握手四次挥手
private:私有的方法是无法继承的
static:详见下面,静态方法非实例方法,不需要对象,直接用类名调用
final:被final修饰的类没有子类
abstract:抽象类,详见下面,不能与上述3个关键字共存
public: 表示成员(类、方法、变量等)是公共的,可以被任何其他类访问。
protected: 表示成员对于同一包中的类以及所有子类可见。 extends: 在类的声明中,用于指定该类继承自另一个类。
implements: 在类的声明中,用于指定该类实现了一个接口。
interface: 声明一个接口。
new: 创建对象实例。
this: 表示当前对象的引用。
super: 详见下面深拷贝,表示父类对象的引用。
synchronized: 用于控制对代码块的访问,确保在同一时刻只有一个线程可以执行。
静态方法(使用 static
关键字声明):属于类,不依赖于对象实例,可以通过类名直接调用。
实例方法(不使用 static
关键字声明):属于类的实例,必须通过对象实例调用。
- public class Example {
- static void staticMethod() {
- System.out.println("Static method");
- }
-
- void instanceMethod() {
- System.out.println("Instance method");
- }
-
- public static void main(String[] args) {
- staticMethod(); // 可以直接调用静态方法
- Example obj = new Example();
- obj.instanceMethod(); // 需要通过对象调用实例方法
- }
- }
成员种类: 抽象类中可以包含常量、抽象方法,也可以包含具体方法(有实现体的方法)。
单继承: 一个类只能继承一个抽象类。Java 中,一个类使用 extends
关键字来继承一个抽象类。
构造方法: 抽象类可以有构造方法,这些构造方法可以被子类调用,用于初始化父类的成员变量。
字段: 抽象类中的字段可以是普通字段,不强制为常量。
- abstract class MyAbstractClass {
- int intValue; // 普通字段
-
- public static final int CONSTANT_VALUE = 20; // 常量
-
- public abstract void abstractMethod(); // 抽象方法
-
- public void concreteMethod() {
- System.out.println("Concrete method in abstract class");
- }
- }
-
- class MyConcreteClass extends MyAbstractClass {
- @Override
- public void abstractMethod() {
- System.out.println("Implementation of abstractMethod");
- }
- }
成员限制: 接口中只能包含常量(constant)和抽象方法(abstract method)。在 Java 中,接口中的成员默认都是公共的、静态的、并且最终的。
多继承: 类可以实现多个接口。这允许一个类在不同的方面具有不同的行为,从而实现了多继承。
实现关键字: 类实现接口时使用 implements
关键字。
构造方法: 接口不能有构造方法,因为接口主要是用于定义规范而不是提供实现。
字段: 接口中的字段默认是常量,且必须被初始化。
- interface MyInterface {
- int CONSTANT_VALUE = 10; // 常量
-
- void abstractMethod(); // 抽象方法
- }
-
- class MyClass implements MyInterface {
- @Override
- public void abstractMethod() {
- System.out.println("Implementation of abstractMethod");
- }
- }
多态的实现要有继承、重写,父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系,增加类的可扩充性和灵活性。
也称为动态多态或晚期绑定。在运行时,根据对象的实际类型确定调用的具体实现。这通常通过方法的重写和接口的实现来实现。
- // 定义一个形状接口
- interface Shape {
- void draw();
- }
-
- // 实现接口的不同形状类
- class Circle implements Shape {
- @Override
- public void draw() {
- System.out.println("Drawing a Circle");
- }
- }
-
- class Square implements Shape {
- @Override
- public void draw() {
- System.out.println("Drawing a Square");
- }
- }
-
- class Triangle implements Shape {
- @Override
- public void draw() {
- System.out.println("Drawing a Triangle");
- }
- }
-
- // 使用多态性
- public class PolymorphismExample {
- public static void main(String[] args) {
- // 使用接口类型的引用指向不同的实现类对象
- Shape shape1 = new Circle();
- Shape shape2 = new Square();
- Shape shape3 = new Triangle();
-
- // 调用draw方法,实际上会调用对应子类的实现
- shape1.draw(); // 输出: Drawing a Circle
- shape2.draw(); // 输出: Drawing a Square
- shape3.draw(); // 输出: Drawing a Triangle
- }
- }
编译时多态和运行时多态是面向对象编程中两个重要的概念,它们分别在编译时和运行时表现出不同的特性。
也称为静态多态或早期绑定。在编译时,编译器就能确定方法或操作符要调用的具体实现,因此它在编译时就能够决定具体的调用。
感觉有点像后面那个重载
- public class CompileTimePolymorphism {
- public int add(int a, int b) {
- return a + b;
- }
-
- public double add(double a, double b) {
- return a + b;
- }
-
- public static void main(String[] args) {
- CompileTimePolymorphism obj = new CompileTimePolymorphism();
-
- int result1 = obj.add(10, 20); // 编译时确定调用 int add(int a, int b)
- double result2 = obj.add(10.5, 20.5); // 编译时确定调用 double add(double a, double b)
- }
- }
定义: 在同一个类中,可以定义多个方法,它们具有相同的名称但参数列表不同(包括参数类型、个数、顺序)的特性被称为方法的重载。
发生位置: 重载发生在同一个类中。
返回类型: 重载方法的返回类型可以相同也可以不同。
示例代码:
- 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 concatenate(String str1, String str2) {
- return str1 + str2;
- }
- }
定义: 子类可以提供一个与其父类中某个方法具有相同名称、相同参数列表和相同返回类型的方法,这被称为方法的重写。
发生位置: 重写发生在子类中,覆盖父类中的方法。
返回类型: 重写方法的返回类型必须与被重写的父类方法相同,或者是其子类。
示例代码:
- class Animal {
- public void makeSound() {
- System.out.println("Animal makes a sound");
- }
- }
-
- class Dog extends Animal {
- @Override
- public void makeSound() {
- System.out.println("Dog barks");
- }
- }
-
- public class Main {
- public static void main(String[] args) {
- // 创建 Animal 对象
- Animal animal = new Animal();
- // 调用 Animal 对象的 makeSound 方法
- animal.makeSound(); // 输出: Animal makes a sound
-
- // 创建 Dog 对象
- Dog dog = new Dog();
- // 调用 Dog 对象的 makeSound 方法
- dog.makeSound(); // 输出: Dog barks
-
- // 使用父类引用指向子类对象
- Animal anotherDog = new Dog();
- // 调用被重写的 makeSound 方法,实际上是调用了 Dog 类中的 makeSound
- anotherDog.makeSound(); // 输出: Dog barks
- }
- }
Java是一种面向对象的语言,但你可以使用它以面向过程的方式编写代码。然而,通过充分利用Java的面向对象特性,你可以更好地组织和抽象代码,提高代码的可维护性和可扩展性。
面向对象的三大特征:封装、继承、多条
- // 定义一个简单的类
- class Car {
- private String brand;
- private String model;
-
- // 构造方法
- public Car(String brand, String model) {
- this.brand = brand;
- this.model = model;
- }
-
- // 方法
- public void start() {
- System.out.println("The " + brand + " " + model + " is starting.");
- }
- }
-
- // 主类
- public class Main {
- public static void main(String[] args) {
- // 创建Car对象
- Car myCar = new Car("Toyota", "Camry");
-
- // 调用方法
- myCar.start();
- }
- }
- // 面向过程的风格
- public class CarProcessor {
- public static void startCar(String brand, String model) {
- System.out.println("The " + brand + " " + model + " is starting.");
- }
- }
-
- // 主类
- public class Main {
- public static void main(String[] args) {
- // 调用面向过程的方法
- CarProcessor.startCar("Toyota", "Camry");
- }
- }
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是关于复制对象时涉及到对象内部数据的两种不同方式。
浅拷贝仅仅复制对象的引用,而不是复制对象本身。因此,原对象和拷贝对象共享相同的内部对象。
- class Person implements Cloneable {
- String name;
- Age age;
-
- public Person(String name, int years) {
- this.name = name;
- this.age = new Age(years);
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
-
- class Age {
- int years;
-
- public Age(int years) {
- this.years = years;
- }
- }
-
- public class ShallowCopyExample {
- public static void main(String[] args) throws CloneNotSupportedException {
- Person person1 = new Person("John", 30);
- Person person2 = (Person) person1.clone();
-
- // 修改原对象的年龄
- person1.age.years = 35;
-
- System.out.println(person1.age.years); // 输出: 35
- System.out.println(person2.age.years); // 输出: 35,因为是浅拷贝,共享同一引用
- }
- }
深拷贝会复制对象本身以及其内部所有引用类型的数据。因此,原对象和拷贝对象拥有各自独立的内部对象。
- class Person implements Cloneable {
- String name;
- Age age;
-
- public Person(String name, int years) {
- this.name = name;
- this.age = new Age(years);
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- // 手动实现深拷贝
- Person clonedPerson = (Person) super.clone();
- clonedPerson.age = (Age) age.clone();
- return clonedPerson;
- }
- }
-
- class Age implements Cloneable {
- int years;
-
- public Age(int years) {
- this.years = years;
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
-
- public class DeepCopyExample {
- public static void main(String[] args) throws CloneNotSupportedException {
- Person person1 = new Person("John", 30);
- Person person2 = (Person) person1.clone();
-
- // 修改原对象的年龄
- person1.age.years = 35;
-
- System.out.println(person1.age.years); // 输出: 35
- System.out.println(person2.age.years); // 输出: 30,因为是深拷贝,拥有独立的引用
- }
- }
主要差别在这个地方
反射(Reflection)是一种在运行时检查和操作类、方法、字段等结构的机制。Java 中的反射机制允许程序在运行时获取类的信息、构造对象、调用方法、访问字段等,而不需要在编译时确定这些信息。
【狂神说Java】注解和反射 我觉得还是看看视频讲解好
反射提供了一个 java.lang.reflect
包,其中包含了类 Class
和一些与类、方法、字段等相关的类,用于在运行时检查和操作类的结构。
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Method;
- import java.lang.reflect.Field;
-
- class MyClass {
- private String privateField;
-
- public MyClass() {
- this.privateField = "Hello, Reflection!";
- }
-
- public void publicMethod() {
- System.out.println("Public method called");
- }
-
- private void privateMethod() {
- System.out.println("Private method called");
- }
- }
-
- public class ReflectionExample {
- public static void main(String[] args) throws Exception {
- // 获取 Class 对象
- Class<?> myClass = MyClass.class;
-
- // 获取构造方法并创建对象
- Constructor<?> constructor = myClass.getConstructor();
- MyClass obj = (MyClass) constructor.newInstance();
-
- // 获取公共方法并调用
- Method publicMethod = myClass.getMethod("publicMethod");
- publicMethod.invoke(obj);
-
- // 获取私有方法并调用
- Method privateMethod = myClass.getDeclaredMethod("privateMethod");
- // 设置私有方法可访问
- privateMethod.setAccessible(true);
- privateMethod.invoke(obj);
-
- // 获取私有字段并访问
- Field privateField = myClass.getDeclaredField("privateField");
- privateField.setAccessible(true);
- System.out.println("Private Field Value: " + privateField.get(obj));
- }
- }
使用new关键字。通常情况下,使用new关键字足以满足大多数对象创建的需求,而其他方式可能在特定情况下更有用。
String str = new String("Hello, World!");
Class.newInstance(),新版clazz.getDeclaredConstructor().newInstance()
- Class<?> clazz = String.class;
- String str = (String) clazz.newInstance();
Constructor.newInstance()
- Constructor<String> constructor = String.class.getConstructor(String.class);
- String str = constructor.newInstance("Hello, World!");
clone()
- StringBuilder original = new StringBuilder("Hello, World!");
- StringBuilder cloned = (StringBuilder) original.clone();
反序列化
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file.ser"));
- out.writeObject("Hello, World!");
- out.close();
-
- ObjectInputStream in = new ObjectInputStream(new FileInputStream("file.ser"));
- String str = (String) in.readObject();
- in.close();
int 的基本数据类型,它是原始的整数类型。Integer 是 int 的包装类,是一个对象。
int 是Java的基本数据类型,属于原始数据类型之一,直接存储整数值。
Integer 是int 的包装类,属于Java的类,它提供了一些额外的功能,例如可以将整数转换为对象,使得整数具有对象的性质。
int 不能为 null。基本数据类型没有引用语义,因此它们不是对象,也不能赋予 null 值。Integer 是一个对象,可以为 null。如果你声明一个 Integer 变量但没有初始化它,它将默认为 null。
int 的操作通常比 Integer 更快,因为它是一个基本数据类型,不需要额外的对象包装和拆包。
Java 提供了自动装箱(Autoboxing)和自动拆箱(Unboxing)机制,允许 int 和 Integer 之间的自动转换。自动装箱是指将基本数据类型自动转换为相应的包装类对象,例如将 int 转换为 Integer。自动拆箱是指将包装类对象自动转换为相应的基本数据类型,例如将 Integer 转换为 int。
进程和线程的区别:
(进程>线程)进程是系统运行的基本单位,线程是独立运行的最小单位,一个进程可以包含多个线程。
进程间通信可以通过管道、共享内存、信号量、消息队列等方式。
线程上下文切换:
当一个线程被剥夺CPU使用权时,切换到另一个线程执行,这过程称为线程上下文切换。
死锁:
死锁是指多个线程在执行过程中,因争夺资源造成的一种相互等待的僵局。
死锁的必要条件包括互斥条件、不可抢占条件、请求和保持条件、循环等待条件。
Synchronized和Lock的区别:
synchronized
是关键字,Lock
是一个类。synchronized
在发生异常时会自动释放锁,Lock
需要手动释放锁。synchronized
是可重入锁、非公平锁、不可中断锁,而 Lock
的 ReentrantLock
是可重入锁、可中断锁,可以是公平锁也可以是非公平锁。ReentrantLock
提供了更多的灵活性,如可中断锁、设置超时等,但相应地增加了代码的复杂性。
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class SynchronizedVsLockExample {
-
- // 共享资源
- private static int counter = 0;
-
- // 使用synchronized关键字
- public synchronized void synchronizedMethod() {
- for (int i = 0; i < 5; i++) {
- counter++;
- System.out.println(Thread.currentThread().getName() + " - Synchronized: " + counter);
- }
- }
-
- // 使用ReentrantLock
- private final Lock lock = new ReentrantLock();
-
- public void lockMethod() {//synchronized 关键字可以直接用在方法上,而 ReentrantLock 则需要显式地调用 lock() 和 unlock() 方法
- lock.lock(); // 获取锁
- try {
- for (int i = 0; i < 5; i++) {
- counter++;
- System.out.println(Thread.currentThread().getName() + " - Lock: " + counter);
- }
- } finally {//使用 synchronized 时,无需手动释放锁,而 ReentrantLock 则需要在 finally 块中手动释放锁
- lock.unlock(); // 释放锁
- }
- }
-
- public static void main(String[] args) {
- final SynchronizedVsLockExample example = new SynchronizedVsLockExample();
-
- // 使用synchronized的线程
- Thread thread1 = new Thread(() -> {
- example.synchronizedMethod();
- });
-
- // 使用ReentrantLock的线程
- Thread thread2 = new Thread(() -> {
- example.lockMethod();
- });
-
- thread1.start();
- thread2.start();
- }
- }
AQS锁:
AQS(AbstractQueuedSynchronizer)是一个抽象类,用于构造锁和同步类。
AQS的原理是通过内部的一个共享模式和一个独占模式,实现对资源的访问。
sleep()和wait()的区别:
sleep()
是 Thread
类的方法,wait()
是 Object
类的方法。sleep()
不释放锁,wait()
会释放锁。wait()
必须在同步方法或同步代码块中执行。
线程池七大参数:
核心线程数、最大线程数、最大线程的存活时间、最大线程的存活时间单位、阻塞队列、线程工厂、任务拒绝策略。
Java内存模型:
JMM(Java内存模型)定义了Java程序中各种变量的访问规则,包括原子性、可见性、有序性。
CAS锁:
CAS(Compare and Swap)是一种原子操作,用于实现多线程环境下的同步操作。
CAS锁可以保证原子性,但不能解决ABA问题。
ThreadLocal原理:
ThreadLocal为每个线程维护了一个独立的变量副本,保证线程之间的隔离。
volatile:
volatile
修饰的变量保证了可见性和有序性,但不保证原子性。
使用 volatile
可以解决一些并发问题,如单例模式中的双重检查锁问题。
线程使用方式:
继承 Thread
类、实现 Runnable
接口、实现 Callable
接口、线程池创建线程。
根据CPU核心数设计线程池线程数量:
IO密集型任务的线程数可以设置为核心数的两倍。
CPU密集型任务的线程数可以设置为CPU核心数。
AtomicInteger的使用场景:
AtomicInteger
提供了原子操作,适用于高并发环境下对整数的操作,不需要额外的锁。
就是关系型数据库和非关系型数据库
这里举的例子使用MySQL存储用户信息和博客文章的关系数据,同时使用MongoDB存储博客文章的评论,因为评论可以是不同结构的半结构化数据。
MySQL部分如下
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
-
- public class MySQLExample {
-
- public static void main(String[] args) {
- String url = "jdbc:mysql://localhost:3306/blog_db";
- String username = "user";
- String password = "password";
-
- try {
- Connection connection = DriverManager.getConnection(url, username, password);
-
- // 假设创建一个用户并发布一篇博客文章
- createUser(connection, "john_doe", "john@example.com");
- createBlogPost(connection, "john_doe", "My First Blog Post", "This is the content of my first blog post.");
-
- connection.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- public static void createUser(Connection connection, String username, String email) throws SQLException {
- String insertUserQuery = "INSERT INTO users (username, email) VALUES (?, ?)";
- PreparedStatement preparedStatement = connection.prepareStatement(insertUserQuery);
- preparedStatement.setString(1, username);
- preparedStatement.setString(2, email);
- preparedStatement.executeUpdate();
- }
-
- public static void createBlogPost(Connection connection, String author, String title, String content) throws SQLException {
- String insertPostQuery = "INSERT INTO blog_posts (author, title, content) VALUES (?, ?, ?)";
- PreparedStatement preparedStatement = connection.prepareStatement(insertPostQuery);
- preparedStatement.setString(1, author);
- preparedStatement.setString(2, title);
- preparedStatement.setString(3, content);
- preparedStatement.executeUpdate();
- }
- }
MongoDB部分如下
- import com.mongodb.MongoClient;
- import com.mongodb.client.MongoCollection;
- import com.mongodb.client.MongoDatabase;
- import org.bson.Document;
-
- public class MongoDBExample {
-
- public static void main(String[] args) {
- MongoClient mongoClient = new MongoClient("localhost", 27017);
- MongoDatabase database = mongoClient.getDatabase("blog_db");
-
- // 假设存储博客文章的评论
- storeBlogPostComment(database, "My First Blog Post", "User1", "Great post!");
- storeBlogPostComment(database, "My First Blog Post", "User2", "I learned a lot from this.");
-
- mongoClient.close();
- }
-
- public static void storeBlogPostComment(MongoDatabase database, String blogPostTitle, String commenter, String comment) {
- MongoCollection
commentsCollection = database.getCollection("comments"); - Document commentDoc = new Document();
- commentDoc.append("blogPostTitle", blogPostTitle)
- .append("commenter", commenter)
- .append("comment", comment);
- commentsCollection.insertOne(commentDoc);
- }
- }
数据定义:
结构化数据:结构化数据是按照明确定义的数据模型组织的数据。通常,结构化数据以表格、行和列的形式存储,遵循预定义的模式,如数据库表中的数据。它具有明确定义的字段和数据类型,通常使用关系型数据库来存储和管理。
非结构化数据:非结构化数据没有明确定义的数据模型。这种数据可能是自由文本、图像、音频、视频、日志文件、社交媒体帖子等。非结构化数据不遵循固定的结构或模式,其内容和格式可以变化。
数据存储:
结构化数据:结构化数据通常以表格或数据库表的形式存储在关系型数据库中。这种数据易于查询、分析和报告,因为字段和值都有固定的结构。
非结构化数据:非结构化数据通常以文件、文档、二进制数据等形式存储在文件系统、NoSQL数据库或其他存储介质中。处理非结构化数据通常需要特定的工具和方法。
数据分析:
结构化数据:结构化数据更容易进行直接的数据分析,因为字段和关系是明确定义的。它适用于传统的商业智能和数据仓库分析。
非结构化数据:非结构化数据的分析通常需要使用自然语言处理、机器学习、图像识别等高级技术。这些数据常用于大数据分析、社交媒体情感分析和图像识别等应用。
设备处理和保存数据的方法大体上分为两种:一种是对保存的数据定期进行采集和处理的批处 理,另一种是将收到的数据逐次进行处理的流处理。
智能家居为了能准确判断房间里有没有人,需要从多个传感器的值所包含的关联性来判断人在或不在房间里。人类凭经验去摸索和决定这种值,机器则靠数据分析。数据分析的代表性方法有两种,分别是统计分析和机器学习。
统计分析:
靠人类手工操作进行的分析,基于统计学原理和方法。它包括描述性统计(例如均值、中位数、标准差)、推断统计(例如假设检验、置信区间)、回归分析、方差分析等。统计分析通常依赖于概率论和数理统计的基本概念。
机器学习:
机器学习就如它的字面意思一样,计算机会按照程序决定的算法, 机械性地学习所给数据之间的联系性。当给出未知数据时,也会输出与其对应的值。机器学习分为两个阶段:学习阶段和识别阶段。
举个例子,假设我们想使用若干种传感器来识别房间里有没有人。这种情况下需要准备两种数据,即房间里有人时的传感器数据(正面例子)和房间里没人时的传感器数据(反面例子)。计算机通过把这两种数据分别交给学习器,可以获取制作鉴别器用的参数。对于以参数为基准制作的鉴别器而言,只要输入从各个感测设备接收到的数据,鉴别器就能输出结果,告诉我们现在房间里是否有人。
上述内容属于机器学习的示例之一,被称作分类问题。在用于执行数据分类的机器学习算法中有很多途径,如用于垃圾邮件过滤器的贝叶斯过滤器和用于分类文档及图像的支持向量机(Support Vector Machine, SVM)等。