No, it doesn't build a cartesian product of the two tables for a inner or outer join.
The database uses a Cartesian join when one or more of the tables does not have any join conditions to any other tables in the statement." Database SQL Tuning Guide
So, in your example, it does a cartesian join only if the where clause is empty:
EXPLAIN PLAN FOR
SELECT a.a1, b.b1 FROM a,b;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 40 | 240 | 10 (0)| 00:00:01 |
| 1 | MERGE JOIN CARTESIAN| | 40 | 240 | 10 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | B | 4 | 12 | 3 (0)| 00:00:01 |
| 3 | BUFFER SORT | | 10 | 30 | 7 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | A | 10 | 30 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
SELECT a.a1, b.b1 FROM a,b;
It normally uses one of three join methods, which are explained in the same document.
In your case, it's using a "hash join" for the inner join:
EXPLAIN PLAN FOR
SELECT a.a1, b.b1 FROM a,b WHERE a.a1 = b.b1;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 24 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 4 | 24 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| B | 4 | 12 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| A | 10 | 30 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."A1"="B"."B1")
and a "hash join outer" for your outer join:
EXPLAIN PLAN FOR
SELECT a.a1, b.b1 FROM a,b WHERE a.a1 = b.b1(+);
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 60 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 10 | 60 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| A | 10 | 30 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| B | 4 | 12 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."A1"="B"."B1"(+))