Since you mentioned things like writing code on paper and similar, I'm going to share some of my experience with you:
First thing you may want to consider is to separate concepts of passing an exam and learning the programming language professors are teaching you. I think that this point is VERY important and here's why: In my faculty we do exams on paper. Usually there will be a couple of "theoretical questions" like Can in Java methods wait(), notify() and notifyAll() of class Object be redefined in sub-classes?
or figure out what this C program prints :
#include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[])
int i, a, b, c = 0, d;
for(i=1;i<argc;i++) {
d=atoi(argv[i]);
if(i==1) a = b = d;
else { if( a<d ) a=d;
if( b>d ) d=d;
c += d; }
printf("%d%d%d", a, b,c);
}
if arguments are 4 6 7 10
or something similar and a program which student has to write on a piece of paper.
I've also analyzed how several students I know studied and how they passed the exam.
We had some people who only studied from text-books, lecture notes and past exams. They usually scored relatively well on the exam because they focused on passing exam. They looked for patterns in exam problems which would often repeat and looked for most efficient ways to solve them from solutions of older exams and lecture notes. They also focused on memorizing as much code as possible and mixed and matched pieces in order to get solutions, basically practicing copy-paste programming. Such people often don't get comfortable using the programing language they learned! So instead of typing a simple C program to solve some repetitive problem, they'd often do it by hand. Also such people are often unfamiliar with ways of gaining information outside university. They haven't found good websites explaining problems they would need to solve in future. Another problem I've noticed is that they are often uncomfortable using some more "advanced" features of integrated development environments like debugging.
On the other hand there are people like me who learned the language by writing code and running it and analyzing it. Problems I've had are basically over-relying on computer. Modern IDEs have good code completion and easy to use debuggers. While I learned how to make programs which work well on a computer, I didn't learn so well how to analyze code without a computer. Simply I had no need to analyze what some über-1337 code does using pen and paper. My fist reaction is to fire up debugger and watch variables as the program executes. OF course if the exam is done on paper, I don't have debugger available. So while I understand in general what piece of code does, I can't quickly figure out what some function does which has several levels of recursion or something similar. That ability needs to be practiced! Another thing I've noticed is that on "practical" part of the exam, I'd often make an original solution which doesn't use pre-made elements from previous exams. They wouldn't be as simple as canonical answers, but they are product of my own work. Also I'm comfortable solving problems programatically and I have managed to pick up good sources of information on Internet and know how to use documentation in popular IDEs.
In the end there are also what I'd call "nerds". Those people would do their best to brute-force their way through exams. The would solve by themselves every past exam, memorize whole book and everything ever said during lectures and they would make tons of simple programs. Of course, they get great score and do learn the stuff, but in order to do that large sacrifices must be made!
So in the end after this wall of text, here's my answer: If you want to pass your exams, you should do your homework and problems in the book and on past exams. If you want to actually learn the language, you should write the code and then analyze it until you have good understanding what it does lastly and find bugs in it. You should find tools which will analyze the code and pint out problems in it, like for example Splint for C programs.