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.