Overview
Creating UML class diagrams from Java code helps visualize the structure, relationships, and design patterns in your application. Here are multiple approaches to generate UML diagrams from Java code.
Manual Approach
1. Basic Class Structure Mapping
// Example Java classes for UML demonstration
public abstract class Vehicle {
private String model;
protected int year;
public static final String TYPE = "VEHICLE";
public Vehicle(String model, int year) {
this.model = model;
this.year = year;
}
public abstract void start();
public String getModel() { return model; }
public void setModel(String model) { this.model = model; }
}
public interface Maintainable {
void performMaintenance();
boolean needsService();
}
public class Car extends Vehicle implements Maintainable {
private int doors;
private Engine engine; // Composition
private List<Wheel> wheels; // Aggregation
public Car(String model, int year, int doors) {
super(model, year);
this.doors = doors;
this.engine = new Engine();
this.wheels = new ArrayList<>();
}
@Override
public void start() {
System.out.println("Car starting...");
engine.ignite();
}
@Override
public void performMaintenance() {
System.out.println("Performing car maintenance");
}
@Override
public boolean needsService() {
return year < 2020;
}
public void addWheel(Wheel wheel) {
wheels.add(wheel);
}
}
public class Engine {
private String type;
private double capacity;
public void ignite() {
System.out.println("Engine ignited");
}
}
public class Wheel {
private int size;
private String position;
}
2. Relationship Analysis
import java.util.*;
import java.time.LocalDate;
// Association relationships
public class University {
private String name;
private List<Department> departments; // Composition
private List<Student> students; // Association
public University(String name) {
this.name = name;
this.departments = new ArrayList<>();
this.students = new ArrayList<>();
}
public void addDepartment(Department department) {
departments.add(department);
}
public void enrollStudent(Student student) {
students.add(student);
}
}
public class Department {
private String name;
private List<Course> courses; // Aggregation
public Department(String name) {
this.name = name;
this.courses = new ArrayList<>();
}
public void addCourse(Course course) {
courses.add(course);
}
}
public class Course {
private String code;
private String title;
private Professor instructor; // Association
private List<Student> enrolledStudents; // Association
public Course(String code, String title) {
this.code = code;
this.title = title;
this.enrolledStudents = new ArrayList<>();
}
public void setInstructor(Professor instructor) {
this.instructor = instructor;
}
public void enrollStudent(Student student) {
enrolledStudents.add(student);
}
}
// Inheritance hierarchy
public abstract class Person {
private String name;
private String email;
public Person(String name, String email) {
this.name = name;
this.email = email;
}
public abstract String getRole();
}
public class Student extends Person {
private String studentId;
private List<Course> courses;
public Student(String name, String email, String studentId) {
super(name, email);
this.studentId = studentId;
this.courses = new ArrayList<>();
}
@Override
public String getRole() {
return "Student";
}
}
public class Professor extends Person {
private String employeeId;
private List<Course> teachingCourses;
public Professor(String name, String email, String employeeId) {
super(name, email);
this.employeeId = employeeId;
this.teachingCourses = new ArrayList<>();
}
@Override
public String getRole() {
return "Professor";
}
}
// Interface implementation
public interface Research {
void conductResearch();
void publishPaper(String title);
}
public class ResearchProfessor extends Professor implements Research {
private String researchArea;
public ResearchProfessor(String name, String email, String employeeId, String researchArea) {
super(name, email, employeeId);
this.researchArea = researchArea;
}
@Override
public void conductResearch() {
System.out.println("Conducting research in " + researchArea);
}
@Override
public void publishPaper(String title) {
System.out.println("Publishing paper: " + title);
}
}
Automated Tools Approach
1. Using PlantUML with Java
// PlantUML Generator from Java Code
import java.lang.reflect.*;
import java.util.*;
public class PlantUMLGenerator {
public String generateClassDiagram(Class<?>... classes) {
StringBuilder plantUML = new StringBuilder();
plantUML.append("@startuml\n");
plantUML.append("title Class Diagram\n\n");
// Generate class definitions
for (Class<?> clazz : classes) {
plantUML.append(generateClassDefinition(clazz));
}
// Generate relationships
for (Class<?> clazz : classes) {
plantUML.append(generateRelationships(clazz, classes));
}
plantUML.append("@enduml");
return plantUML.toString();
}
private String generateClassDefinition(Class<?> clazz) {
StringBuilder classDef = new StringBuilder();
// Class type
if (clazz.isInterface()) {
classDef.append("interface ");
} else if (Modifier.isAbstract(clazz.getModifiers())) {
classDef.append("abstract class ");
} else {
classDef.append("class ");
}
classDef.append(clazz.getSimpleName()).append(" {\n");
// Fields
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
classDef.append(" ")
.append(getVisibility(field.getModifiers()))
.append(field.getType().getSimpleName())
.append(" ")
.append(field.getName())
.append("\n");
}
}
// Methods (simplified)
for (Method method : clazz.getDeclaredMethods()) {
if (method.getParameterCount() == 0 &&
Modifier.isPublic(method.getModifiers()) &&
!method.getName().startsWith("get") &&
!method.getName().startsWith("set")) {
classDef.append(" ")
.append(getVisibility(method.getModifiers()))
.append(method.getReturnType().getSimpleName())
.append(" ")
.append(method.getName())
.append("()\n");
}
}
classDef.append("}\n\n");
return classDef.toString();
}
private String generateRelationships(Class<?> clazz, Class<?>[] allClasses) {
StringBuilder relationships = new StringBuilder();
// Inheritance
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && Arrays.asList(allClasses).contains(superclass)) {
relationships.append(superclass.getSimpleName())
.append(" <|-- ")
.append(clazz.getSimpleName())
.append("\n");
}
// Interface implementation
for (Class<?> interfaceClass : clazz.getInterfaces()) {
if (Arrays.asList(allClasses).contains(interfaceClass)) {
relationships.append(interfaceClass.getSimpleName())
.append(" <|.. ")
.append(clazz.getSimpleName())
.append("\n");
}
}
// Associations (simplified - based on field types)
for (Field field : clazz.getDeclaredFields()) {
Class<?> fieldType = field.getType();
if (Arrays.asList(allClasses).contains(fieldType) &&
!fieldType.equals(clazz)) {
relationships.append(clazz.getSimpleName())
.append(" --> ")
.append(fieldType.getSimpleName())
.append(" : ")
.append(field.getName())
.append("\n");
}
}
return relationships.toString();
}
private String getVisibility(int modifiers) {
if (Modifier.isPublic(modifiers)) return "+";
if (Modifier.isProtected(modifiers)) return "#";
if (Modifier.isPrivate(modifiers)) return "-";
return "~"; // package-private
}
public static void main(String[] args) throws Exception {
PlantUMLGenerator generator = new PlantUMLGenerator();
String plantUML = generator.generateClassDiagram(
Vehicle.class, Car.class, Maintainable.class,
Engine.class, Wheel.class
);
System.out.println(plantUML);
}
}
2. UML Diagram Generator
import java.util.*;
import java.lang.reflect.*;
public class UMLDiagramGenerator {
public static class ClassInfo {
private Class<?> clazz;
private List<FieldInfo> fields = new ArrayList<>();
private List<MethodInfo> methods = new ArrayList<>();
private List<Relationship> relationships = new ArrayList<>();
public ClassInfo(Class<?> clazz) {
this.clazz = clazz;
analyzeClass();
}
private void analyzeClass() {
// Analyze fields
for (Field field : clazz.getDeclaredFields()) {
fields.add(new FieldInfo(field));
analyzeFieldRelationships(field);
}
// Analyze methods
for (Method method : clazz.getDeclaredMethods()) {
if (isRelevantMethod(method)) {
methods.add(new MethodInfo(method));
}
}
// Analyze inheritance
analyzeInheritance();
analyzeInterfaces();
}
private void analyzeFieldRelationships(Field field) {
Class<?> fieldType = field.getType();
if (!fieldType.isPrimitive() && !fieldType.getName().startsWith("java.")) {
Relationship relationship = new Relationship();
relationship.targetClass = fieldType.getSimpleName();
relationship.type = fieldType.isInterface() ?
RelationshipType.DEPENDENCY : RelationshipType.ASSOCIATION;
relationship.fieldName = field.getName();
relationships.add(relationship);
}
}
private void analyzeInheritance() {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !superClass.equals(Object.class)) {
Relationship relationship = new Relationship();
relationship.targetClass = superClass.getSimpleName();
relationship.type = RelationshipType.INHERITANCE;
relationships.add(relationship);
}
}
private void analyzeInterfaces() {
for (Class<?> interfaceClass : clazz.getInterfaces()) {
Relationship relationship = new Relationship();
relationship.targetClass = interfaceClass.getSimpleName();
relationship.type = RelationshipType.IMPLEMENTATION;
relationships.add(relationship);
}
}
private boolean isRelevantMethod(Method method) {
int modifiers = method.getModifiers();
return Modifier.isPublic(modifiers) &&
!method.getName().equals("equals") &&
!method.getName().equals("hashCode") &&
!method.getName().equals("toString");
}
// Getters
public String getClassName() { return clazz.getSimpleName(); }
public List<FieldInfo> getFields() { return fields; }
public List<MethodInfo> getMethods() { return methods; }
public List<Relationship> getRelationships() { return relationships; }
public boolean isInterface() { return clazz.isInterface(); }
public boolean isAbstract() { return Modifier.isAbstract(clazz.getModifiers()); }
}
public static class FieldInfo {
private String name;
private String type;
private String visibility;
public FieldInfo(Field field) {
this.name = field.getName();
this.type = field.getType().getSimpleName();
this.visibility = getVisibilitySymbol(field.getModifiers());
}
// Getters
public String getName() { return name; }
public String getType() { return type; }
public String getVisibility() { return visibility; }
}
public static class MethodInfo {
private String name;
private String returnType;
private String visibility;
private List<String> parameters = new ArrayList<>();
public MethodInfo(Method method) {
this.name = method.getName();
this.returnType = method.getReturnType().getSimpleName();
this.visibility = getVisibilitySymbol(method.getModifiers());
for (Parameter param : method.getParameters()) {
parameters.add(param.getType().getSimpleName() + " " + param.getName());
}
}
// Getters
public String getName() { return name; }
public String getReturnType() { return returnType; }
public String getVisibility() { return visibility; }
public List<String> getParameters() { return parameters; }
}
public static class Relationship {
public String targetClass;
public RelationshipType type;
public String fieldName;
}
public enum RelationshipType {
INHERITANCE, IMPLEMENTATION, ASSOCIATION, DEPENDENCY, COMPOSITION, AGGREGATION
}
private static String getVisibilitySymbol(int modifiers) {
if (Modifier.isPublic(modifiers)) return "+";
if (Modifier.isProtected(modifiers)) return "#";
if (Modifier.isPrivate(modifiers)) return "-";
return "~";
}
public void generateDiagram(Class<?>... classes) {
List<ClassInfo> classInfos = new ArrayList<>();
// Analyze all classes
for (Class<?> clazz : classes) {
classInfos.add(new ClassInfo(clazz));
}
// Generate diagram
printClassDiagram(classInfos);
}
private void printClassDiagram(List<ClassInfo> classInfos) {
System.out.println("=== UML CLASS DIAGRAM ===\n");
// Print classes
for (ClassInfo classInfo : classInfos) {
printClass(classInfo);
}
// Print relationships
System.out.println("\n=== RELATIONSHIPS ===");
for (ClassInfo classInfo : classInfos) {
for (Relationship relationship : classInfo.getRelationships()) {
printRelationship(classInfo, relationship);
}
}
}
private void printClass(ClassInfo classInfo) {
System.out.println("\n" + getClassTypeSymbol(classInfo) + " " + classInfo.getClassName());
System.out.println("---");
// Fields
for (FieldInfo field : classInfo.getFields()) {
System.out.println(field.getVisibility() + " " +
field.getType() + " " + field.getName());
}
if (!classInfo.getFields().isEmpty() && !classInfo.getMethods().isEmpty()) {
System.out.println("---");
}
// Methods
for (MethodInfo method : classInfo.getMethods()) {
System.out.println(method.getVisibility() + " " +
method.getReturnType() + " " +
method.getName() + "(" +
String.join(", ", method.getParameters()) + ")");
}
}
private void printRelationship(ClassInfo source, Relationship relationship) {
String arrow = getRelationshipArrow(relationship.type);
System.out.println(source.getClassName() + " " + arrow + " " +
relationship.targetClass +
(relationship.fieldName != null ?
" : " + relationship.fieldName : ""));
}
private String getClassTypeSymbol(ClassInfo classInfo) {
if (classInfo.isInterface()) return "«interface»";
if (classInfo.isAbstract()) return "«abstract»";
return "class";
}
private String getRelationshipArrow(RelationshipType type) {
switch (type) {
case INHERITANCE: return "▷";
case IMPLEMENTATION: return "▷--";
case ASSOCIATION: return "——";
case DEPENDENCY: return "⤳";
case COMPOSITION: return "◆—";
case AGGREGATION: return "◇—";
default: return "——";
}
}
public static void main(String[] args) throws Exception {
UMLDiagramGenerator generator = new UMLDiagramGenerator();
generator.generateDiagram(
University.class, Department.class, Course.class,
Person.class, Student.class, Professor.class,
ResearchProfessor.class, Research.class
);
}
}
Real-World Example: E-commerce System
// E-commerce Domain Classes
public abstract class User {
private String userId;
private String name;
private String email;
private Date createdAt;
public User(String userId, String name, String email) {
this.userId = userId;
this.name = name;
this.email = email;
this.createdAt = new Date();
}
public abstract String getRole();
// Getters and setters
public String getUserId() { return userId; }
public String getName() { return name; }
public String getEmail() { return email; }
}
public class Customer extends User {
private List<Order> orders;
private ShoppingCart cart;
private String shippingAddress;
public Customer(String userId, String name, String email) {
super(userId, name, email);
this.orders = new ArrayList<>();
this.cart = new ShoppingCart();
}
@Override
public String getRole() {
return "CUSTOMER";
}
public void placeOrder() {
Order order = new Order(this, cart.getItems());
orders.add(order);
cart.clear();
}
}
public class Admin extends User {
private String adminLevel;
public Admin(String userId, String name, String email, String adminLevel) {
super(userId, name, email);
this.adminLevel = adminLevel;
}
@Override
public String getRole() {
return "ADMIN";
}
public void manageProducts() {
System.out.println("Managing products...");
}
}
public class Product {
private String productId;
private String name;
private String description;
private double price;
private int stockQuantity;
private Category category;
public Product(String productId, String name, double price) {
this.productId = productId;
this.name = name;
this.price = price;
}
// Getters and setters
}
public class Category {
private String categoryId;
private String name;
private String description;
private List<Product> products;
public Category(String categoryId, String name) {
this.categoryId = categoryId;
this.name = name;
this.products = new ArrayList<>();
}
public void addProduct(Product product) {
products.add(product);
product.setCategory(this);
}
}
public class ShoppingCart {
private List<CartItem> items;
private Customer customer;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Product product, int quantity) {
items.add(new CartItem(product, quantity));
}
public double getTotalPrice() {
return items.stream()
.mapToDouble(CartItem::getTotalPrice)
.sum();
}
public List<CartItem> getItems() { return items; }
public void clear() { items.clear(); }
}
public class CartItem {
private Product product;
private int quantity;
public CartItem(Product product, int quantity) {
this.product = product;
this.quantity = quantity;
}
public double getTotalPrice() {
return product.getPrice() * quantity;
}
}
public class Order {
private String orderId;
private Customer customer;
private List<OrderItem> items;
private OrderStatus status;
private Date orderDate;
private Payment payment;
public Order(Customer customer, List<CartItem> cartItems) {
this.orderId = generateOrderId();
this.customer = customer;
this.items = new ArrayList<>();
this.status = OrderStatus.PENDING;
this.orderDate = new Date();
for (CartItem cartItem : cartItems) {
items.add(new OrderItem(cartItem.getProduct(), cartItem.getQuantity()));
}
}
private String generateOrderId() {
return "ORD-" + System.currentTimeMillis();
}
public void processPayment(Payment payment) {
this.payment = payment;
this.status = OrderStatus.PAID;
}
}
public class OrderItem {
private Product product;
private int quantity;
private double unitPrice;
public OrderItem(Product product, int quantity) {
this.product = product;
this.quantity = quantity;
this.unitPrice = product.getPrice();
}
}
public enum OrderStatus {
PENDING, PAID, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}
public interface Payment {
boolean processPayment(double amount);
String getPaymentDetails();
}
public class CreditCardPayment implements Payment {
private String cardNumber;
private String cardHolder;
private String expiryDate;
private String cvv;
public CreditCardPayment(String cardNumber, String cardHolder, String expiryDate, String cvv) {
this.cardNumber = cardNumber;
this.cardHolder = cardHolder;
this.expiryDate = expiryDate;
this.cvv = cvv;
}
@Override
public boolean processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
return true;
}
@Override
public String getPaymentDetails() {
return "Credit Card ending with " + cardNumber.substring(cardNumber.length() - 4);
}
}
public class PayPalPayment implements Payment {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public boolean processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount + " for " + email);
return true;
}
@Override
public String getPaymentDetails() {
return "PayPal: " + email;
}
}
// UML Generator for E-commerce system
public class ECommerceUMLGenerator {
public static void main(String[] args) throws Exception {
UMLDiagramGenerator generator = new UMLDiagramGenerator();
generator.generateDiagram(
User.class, Customer.class, Admin.class,
Product.class, Category.class,
ShoppingCart.class, CartItem.class,
Order.class, OrderItem.class, OrderStatus.class,
Payment.class, CreditCardPayment.class, PayPalPayment.class
);
}
}
Advanced Features
1. Dependency Analysis
public class DependencyAnalyzer {
public Map<String, Set<String>> analyzeDependencies(Class<?>... classes) {
Map<String, Set<String>> dependencies = new HashMap<>();
for (Class<?> clazz : classes) {
Set<String> classDependencies = new HashSet<>();
analyzeClassDependencies(clazz, classDependencies);
dependencies.put(clazz.getSimpleName(), classDependencies);
}
return dependencies;
}
private void analyzeClassDependencies(Class<?> clazz, Set<String> dependencies) {
// Analyze field dependencies
for (Field field : clazz.getDeclaredFields()) {
Class<?> fieldType = field.getType();
if (!fieldType.isPrimitive() && !fieldType.getName().startsWith("java.")) {
dependencies.add(fieldType.getSimpleName());
}
}
// Analyze method parameter and return type dependencies
for (Method method : clazz.getDeclaredMethods()) {
// Return type
Class<?> returnType = method.getReturnType();
if (!returnType.isPrimitive() && !returnType.getName().startsWith("java.")) {
dependencies.add(returnType.getSimpleName());
}
// Parameter types
for (Parameter param : method.getParameters()) {
Class<?> paramType = param.getType();
if (!paramType.isPrimitive() && !paramType.getName().startsWith("java.")) {
dependencies.add(paramType.getSimpleName());
}
}
}
}
public void printDependencyMatrix(Map<String, Set<String>> dependencies) {
System.out.println("\n=== DEPENDENCY MATRIX ===");
System.out.print("Class\t\t");
List<String> classNames = new ArrayList<>(dependencies.keySet());
for (String className : classNames) {
System.out.print(className + "\t");
}
System.out.println();
for (String sourceClass : classNames) {
System.out.print(sourceClass + "\t\t");
Set<String> sourceDeps = dependencies.get(sourceClass);
for (String targetClass : classNames) {
if (sourceDeps.contains(targetClass)) {
System.out.print("X\t");
} else {
System.out.print(".\t");
}
}
System.out.println();
}
}
}
Best Practices
- Keep diagrams focused on specific domains or modules
- Use consistent naming conventions
- Show only relevant relationships to avoid clutter
- Group related classes using packages or modules
- Update diagrams regularly as code evolves
- Use automated tools for large codebases
- Document design patterns and architectural decisions
These approaches provide comprehensive ways to generate UML class diagrams from Java code, from simple manual analysis to automated generation using reflection and specialized tools.