If you "Check out" Ehsan's example you'll find Jack is retrieved even though he did take the class and, even though Bob did not take the class, he is not retrieved.
"Stupid people compose a query and expect other unusual result "
Vladan's variant 1 example returns the correct results because the ExamName criteria is in the join, NOT the where clause. This causes all StudentExam columns in the join's result set to be NULL for students without a 'SQL Server' exam. The example then filters out the non-null rows from the result set with the where clause.
In Ehsan's example, putting the ExamName <> 'SQL Server' criteria in the where clause filters out all rows where ExamName = 'SQL Server'. This allows Jack to still be retrieved because there is a row in the result set where ExamName <> 'SQL Server'.
This also causes Bob to be filtered out because all of the StudentExam columns in the result set are NULL for Bob (who didn't take any exams). ANY COMPARISON OF A NON-NULL TO A NULL IS NULL. So, ExamName <> 'SQL Server' evaluates to NULL even though it seems like it should evaluate to TRUE and the row is filtered out. Bye bye Bob.
There's more good stuff in BOL on NULL comparisons in the "Comparison Search Conditions" subtopic under "null values" in the index.
Finally, Ehsan's example will return duplicates for students taking more than one exam other than 'SQL Server'. The beauty of Vladan's example is that you only get one row in the result set for each row in the "left" table without a match in the "right" table so "select distinct" is not required.