MySQL LEFT OUTER JOIN performance on a local column condition
It is not 'proper' to have redundant information in a database.  has_cars is redundant with EXISTS(SELECT 1 FROM cars WHERE user_id = ...).

So, once you eliminate has_cars, you are left with just a third option:

SELECT users.*, cars.*
    FROM users
    JOIN cars  ON cars.user_id = users.id


which is the preferred query.

Do not use LEFT unless you are hoping to 'find' rows missing from the right-hand table.  In that case, use the LEFT JOIN ... IS NULL pattern:

SELECT users.name  'Does not have any cars'
    FROM users
    LEFT JOIN cars  ON cars.user_id = users.id
    WHERE cars.id IS NULL;          -- to list _only_ those without cars


Or to get data from cars whether or not there is a row:

SELECT users.name,
       cars.description  -- will be NULL if no cars
    FROM users
    LEFT JOIN cars  ON cars.user_id = users.id;


Another variant:

SELECT users.name,
       COUNT(cars.id)  AS 'how many cars owned (incl 0)'
    FROM users
    LEFT JOIN cars  ON cars.user_id = users.id
    GROUP BY users.id;


This is equivalent to that:

SELECT name,
       ( SELECT COUNT(cars.id) FROM cars  WHERE user_id = users.id
             )  AS 'how many cars owned (incl 0)'
    FROM users;


See also GROUP_CONCAT() as a way of listing (in a single cell) all the cars owned by each person.