If your tables are not accessed by others concurrently (yet), you could use a FULL [OUTER] JOIN query to create a new, merged table, with values from bar getting priority.
If you have concurrent access, but can afford to lock both tables, that works, too:
BEGIN;
LOCK foo, bar;                     -- if you need it
CREATE TABLE baz AS
SELECT a, COALESCE(b.b, a.b) AS b  -- bar gets priority
FROM   foo f
FULL   JOIN bar b USING (a)
ORDER  BY a;                       -- optional order by
-- you need table name foo?
DROP  TABLE foo, bar;
ALTER TABLE baz RENAME TO foo;
ALTER TABLE foo ADD CONSTRAINT foo_a_pkey PRIMARY KEY (a);
-- do more?
COMMIT;
If you have substantial overlap, it's more efficient to write a new, pristine (clustered) table without dead rows, than to update much of the old. If the overlap is not big, update / insert may be more efficient. If both tables are small, don't bother and go with the simple solution.
The new table does not have any of the indexes or constraint of the old ones obviously. Recreate what you need.
If you have a lot of depending objects (views, functions), you may want to keep your old table. Create a temp table instead, TRUNCATE foo and write back data into the same table. This also won't kill concurrent transactions waiting for the existing table.
BEGIN;
LOCK foo, bar;                     -- if you need it
SET temp_buffers = 500MB;          -- if you need it
CREATE TEMP TABLE tmp AS
SELECT a, COALESCE(b.b, a.b) AS b  -- bar gets priority
FROM   foo f
FULL   JOIN bar b USING (a);
-- for performance, you might want to drop indexes and constraints here ...
TRUNCATE foo;
INSERT INTO foo
SELECT * FROM tmp
ORDER BY a;                         -- optional
DROP  TABLE  bar;                   -- optional
-- ... and recreate previously dropped indexes and constraints here
COMMIT;
About temp_buffers:
What happens to concurrent writes?