a)
List l = new ArrayList<String>(); is the same as List<Object> l = new ArrayList<String>();. It means that the variable l is a list that can store any Object, but not only a String. Also you will need to cast the values of the list if you want to use them as Strings. So the following code would compile (which is usually not what you want):
List l = new ArrayList<String>();
l.add("a string");//adding a String type is what is expected here
l.add(42);//adding an int is not realy expected here, but works
l.add(new Date());//also adding any other object is allowed
String s = (String) l.get(0);//s will be "a string"
String s2 = (String) l.get(1);//runtime error, since 42 can't be cast into a String
b)
List<String> l=new ArrayList<String>(); means, that the variable l is a list of Strings and will only accept String values. The following code would cause compiler errors:
List<String> l = new ArrayList<String>();
l.add("a string");//adding a String type is what is expected here
l.add(42);//compiler error
l.add(new Date());//compiler error
c)
List<String> l=new ArrayList<>(); is basically the same as List<String> l=new ArrayList<String>();. The diamond operator <> just means that it should be clear to the compiler, which type is used here, so the programmer can be lazy and let the compiler do the work.
p)
List<String>l = new ArrayList<String>(); tells the compiler, that l is of type List. So if you want to assign an ArrayList to the variable l, that is allowed, and if you then decide, that a LinkedList is better, you can still assign a LinkedList and it will still work, because LinkedList and ArrayList both implement the interface List. So the follwing code will work:
List<String> l = new ArrayList<String>();
//later on you decide to change the type (maybe for a better performance or whatever)
l = new LinkedList<String>();
//use the List l like nothing has changed. You just don't need to care.
q)
ArrayList<String> l= new ArrayList<String>(); means, that the variable l is an ArrayList and can never be any other List implementation (like a LinkedList), so you can't change the implementation easily. Therefore the following code will not compile:
List<String> l = new ArrayList<String>();
//later on you decide to change the type (maybe for a better performance or whatever)
l = new LinkedList<String>();//compiler error; now you need to change many parts of your code if you still want to change the type of l to a LinkedList
Therefore usually p is the better solution, because you can change the code easily. Only of you need to call methods that are not part of the List interface, like the trimToSize method of ArrayList it can be needed to use q. But that's a very rare case.
Summary:
In almost every case it's better to use p than q.
Also in almost every case it's better to use b or c than a. Whether you use b or c doesn't realy matter that much. c is just a bit shorter.