Duplicating in for loop causes weird multiplication
You don't need to select the object before copying it, so bpy.data.objects['Road'].select_set(True) is redundant. 

Instead of that you need to make the newly created object active before calling any bpy.ops to make the op function operate on the new object. But I guess, you can avoid call to ops altogether and simply call newroad.modifiers.new to create the modifier. 

So that the modified code will look like this (this is not tested)...

def execute(self, context):
    objects = bpy.context.scene.objects
    road = bpy.data.objects["Road"]

    for obj in objects:
        if obj.type == "CURVE":
            # road = bpy.data.objects["Road"] # Already set above so not needed
            # bpy.data.objects['Road'].select_set(True) # Not needed

            newroad = road.copy()
            newroad.data = road.data.copy() # You might also want to copy the data block
            bpy.context.collection.objects.link(newroad)

            # bpy.ops.object.modifier_add(type='ARRAY')
            # bpy.ops.object.modifier_add(type='CURVE')

            # arrmod = bpy.context.object.modifiers["Array"]
            # curvemod = bpy.context.object.modifiers["Curve"]

            arrayModName = 'array'
            arrmod = newroad.modifiers.new(arrayModName, 'ARRAY')

            curveModName = 'curve'
            curvemod = newroad.modifiers.new(curveModName, 'CURVE')

            arrmod.fit_type = 'FIT_CURVE'

            # arrmod.curve = bpy.data.objects[obj.name]
            # curvemod.object = bpy.data.objects[obj.name]

            # obj is the same as bpy.data.objects[obj.name]
            arrmod.curve = obj 
            curvemod.object = obj

    return{'FINISHED'}