Doubly linked list in Java
Bug


    public void insertEnd(int data){

        Node newNode = new Node(data);
        newNode.setNext(null);

        if(tail != null){
            tail.setNext(newNode);
            newNode.setPrev(tail);
            tail = newNode;
        }
        if(head == null){
            head = newNode;
        }

        length++;
    }



If tail is null at the beginning, it will stay null.  Like with head in insertFront, you should always set tail in insertEnd.  As is, if you call insertEnd and then insertFront, it will point tail at the one inserted at the front.  

    public void insertEnd(int data){
        Node newNode = new Node(data, tail, null);

        if (tail != null) {
            tail.setNext(newNode);
        }
        tail = newNode;
        if (head == null) {
            head = newNode;
        }

        length++;
    }


This way, it always sets tail to newNode.  

You also never set the previous node if tail is null.  But you explicitly set the next node to null.  This version always sets both.  It would also work to never set the next node and only set the previous node if there is one.  But please don't mix the two notions.  Either set everything or let everything that can default, default.  Either is readable on its own.  Mixed, it gets a bit confusing.  

I tend to favor explicitly setting everything.  

I also prefer more whitespace, so that keywords like if are separated from things like parenthetical expressions.  The exception being method calls.  That way if I see foo(, I know immediately that foo is a method and not some language construct.  

Don't overcomplicate things


    public void removeFront(){

        if(head != null){

            head.getNext().setPrev(null);
            head = head.getNext();

        }else if(head == null){

            System.out.println("List is empty");
            return ;

        }
        length--;
    }



You check both if head is not null and if it is null.  You can check just one.  Further, since you return if it is null, you can do that first.  

    public void removeFront() {
        if (head == null) {
            System.out.println("List is empty");
            return;
        }

        head = head.getNext();
        head.setPrev(null);
        length--;
    }


Now we take care of the exceptional case first, and we don't have to gate the rest of the method.  Note that it would usually make more sense to throw an exception here.  A Deque would throw a NoSuchElementException in that situation.  

If we switch the order of the statements, we don't have to write head.getNext() twice.  

It's no longer confusing that we decrement length outside the if structure but only actually reach that code in one branch.  

Bug 2


    public void removeEnd(){

        if(tail != null){

            System.out.println("Tail not null");
            Node temp = tail.getPrev();
            temp.setNext(null);
            tail = temp;

        }else{
            System.out.println("Tail is null");
        }
        length--;
    }



You decrement length even if there's nothing there.  

    public void removeEnd() {
        if (tail == null) {
            System.out.println("Tail is null");
            return;
        }

        System.out.println("Tail not null");

        tail = tail.getPrev();
        tail.setNext(null);
        length--;
    }


This should follow the same pattern as removeFront.  

Implement an interface

Note that a Deque has a similar API to this.  Consider implementing that interface for compatibility reasons.  Of course, you may find that that is unnecessary for what you need.