Your proposed regular expression requires digits to occur at the end of the string. It also does not allow lower-case letters to occur after any internal upper-case letters. It forces the second character to be a lower-case letter. None of these restrictions was part of the list of conditions.
You have four different conditions on some string $pw. It makes the most sense to try them one after the other. It makes the most sense as the tests are easier to write and understand, we're free to modify the restrictions separate from each other, and we would more easily be able to tell the user which ones of the conditions the string does not pass if we needed to. Doing the tests in sequence also allows us to add new conditions easily, like "must contain a punctuation character", and "must not contain three lower-case letters in a row".
if [[ $pw == [[:upper:]]* ]] &&
[[ $pw == *[[:lower:]]* ]] &&
[[ $pw == *[[:digit:]]* ]] &&
[ "${#pw}" -ge 8 ] && [ "${#pw}" -lt 16 ]
then
echo valid
else
echo invalid
fi
The code above doesn't use regular expressions as it's not needed, and it assumes that "number" means "digit" as opposed to "any number in any notation."
Note that it's not just challenging to do the length test as part of a single regular expression; it is also unreasonably tricky to, at the same time, make sure that the string contains at least one lower-case character and a digit in any order. You may possibly do this in one expression, but it would be awkward.