Your regular expression is not anchored which means that [^;]* will still match against all characters up to a possible ; (and thus match as a whole). If you anchor the regex against the end of the line ([^;]*$) it will produce the results you are after:
$ cat t.sh
#!/bin/bash
pattern='class [^;]*$'
while read -r line; do
printf "testing '${line}': "
[[ $line =~ $pattern ]] && echo matches || echo "doesn't match"
done <<EOT
class test
class test;
class test; // test class
EOT
$ ./t.sh
testing 'class test': matches
testing 'class test;': doesn't match
testing 'class test; // test class': doesn't match
TL;DR: In other words, the bold part in
class test; foo bar quux
matches your regex even though the string contains a semicolon which is why it always matches. The anchor makes sure that the regular expression only matches if there is no semicolon until the very end of the string.
class test // test class;?