Fundamental OOP Concepts and Design Patterns in Java
This article explains core object‑oriented concepts such as encapsulation, abstraction, inheritance and polymorphism, then details common design principles and a wide range of design patterns—including factory, builder, bridge, singleton, strategy, adapter, and proxy—illustrated with complete Java code examples and usage guidelines.
The article begins with the basic OOP concepts: encapsulation hides implementation details and exposes only necessary methods; abstraction defines method signatures without revealing their bodies; inheritance establishes an is‑a relationship between classes; and polymorphism allows a parent type to be substituted by multiple child types, improving extensibility and reuse.
It then presents several design principles: Single Responsibility Principle (SRP) , Interface Segregation Principle (ISP) , Dependency Inversion Principle (DIP) , Liskov Substitution Principle (LSP) , Open‑Closed Principle (OCP) , Demeter Principle , and Composite Reuse Principle . These principles guide the creation of maintainable and flexible software.
Factory patterns are introduced next. The Simple Factory creates product instances based on a parameter, while the Factory Method defines an interface for object creation and lets subclasses decide which class to instantiate. Sample Java code shows an abstract Video class, concrete JavaVideo and PythonVideo subclasses, and corresponding factory classes.
Other creational patterns are briefly covered: the Builder pattern for constructing complex objects step by step, and the Bridge pattern which separates abstraction from implementation using composition.
The article then dives into Singleton patterns . It describes the eager initialization, lazy initialization, double‑checked locking, static inner‑class, and enum‑based singletons, providing Java implementations for each and discussing their advantages, drawbacks, and thread‑safety concerns (including the need for the volatile keyword to avoid the double‑checked locking issue).
Next, structural patterns such as Adapter (class and object adapters) and Proxy are explained. The Adapter section shows how to convert incompatible interfaces using inheritance or composition, with full Java code for ITarget , Adaptee , and Adaptor . The Proxy section covers static proxy, JDK dynamic proxy (including the five steps to create a proxy and an example using InvocationHandler ), and CGLIB dynamic proxy (with a custom MethodInterceptor and example using Enhancer ).
A comparison table summarizes the three proxy approaches (JDK static, JDK dynamic, CGLIB) by implementation method, advantages, disadvantages, and characteristics.
Finally, the article notes how Spring chooses between JDK dynamic proxies (when a bean implements an interface) and CGLIB (when it does not), and mentions that CGLIB can be forced if desired.
public abstract class Video {
public abstract void produce();
}
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}
public abstract class VideoFactory {
public abstract Video getVideo();
}
public class JavaVideoFactory extends VideoFactory {
public Video getVideo() {
return new JavaVideo();
}
}
public class PythonVideoFactory extends VideoFactory {
public Video getVideo() {
return new PythonVideo();
}
}
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new JavaVideoFactory();
Video video = videoFactory.getVideo();
video.produce();
}
} public class SingletonObject {
private static final SingletonObject instance = new SingletonObject();
private SingletonObject() {}
public static SingletonObject getInstance() { return instance; }
} public class SingletonObject {
private static volatile SingletonObject instance;
private SingletonObject() {}
public static SingletonObject getInstance() {
if (instance == null) {
synchronized (SingletonObject.class) {
if (instance == null) {
instance = new SingletonObject();
}
}
}
return instance;
}
} public enum Singleton {
INSTANCE;
private final SingletonObject instance;
Singleton() { instance = new SingletonObject(); }
public SingletonObject getInstance() { return instance; }
} public interface ITarget {
void f1();
void f2();
void fc();
}
public class Adaptee {
public void fa() { /* ... */ }
public void fb() { /* ... */ }
public void fc() { /* ... */ }
}
public class Adaptor extends Adaptee implements ITarget {
public void f1() { super.fa(); }
public void f2() { /* custom implementation */ }
// fc() inherited from Adaptee
} public class TransactionHandler implements UserDao {
private UserDao target;
public TransactionHandler(UserDao target) { this.target = target; }
@Override
public void save() {
System.out.println("开启事务控制...");
target.save();
System.out.println("关闭事务控制...");
}
} public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------前置通知------");
Object result = method.invoke(target, args);
System.out.println("------后置通知------");
return result;
}
} public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("======前置通知======");
Object result = proxy.invokeSuper(obj, args);
System.out.println("======后置通知======");
return result;
}
}Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.