java的访问控制是停留在编译层的,也就是它不会在.class文件中留下任何的痕迹,只在编译的时候进行访问控制的检查。其实,通过反射的手段,是可以访问任何包下任何类中的成员,例如,访问类的私有成员也是可能的

1. 我的问题

对于protected,网上给的作用权限是这样说的:被 protected 修饰的成员对于本包和其子类可见,这样的说法太含糊,对我也造成了不小的困扰

java 对 protected 字段赋值 java中protected的作用_父类


clone方法在Object类中已经实现了,而Person又继承自Object,那么调用自然没有问题,但是这里却报了错

java 对 protected 字段赋值 java中protected的作用_构造函数_02


注意这里Object类中的clone方法是protected级别的,下面来分析一下protected访问修饰符

2. protected

先看第一点:根据网上的说法,protected 的可见地方是这个class内以及它的子类,所以需要让子类能修改的方法,变成protected


class Animal {  
	private void eat(){ 
	} 
}

所以的动物都能吃东西,所以像上面定义成private是不对的,无法扩展

class Animal {  
	protected void eat(){ 
	} 
}

那定义成protected呢?protected还是属于内部方法,不像public那样可以被别的类调用,主要为了实现信息隐藏,那我们吃东西别人肯定能看到能访问,所以应该定义成public

class Animal {  
	public void eat(){ 
	} 
}

根据上面的思路,我们子类内部和父类有共性而且能扩展但是不让外界无关人士访问的方法我们可以定义为protected,最典型的是我们的消化功能,每个动物都能消化,但是这个功能和我没共性的人是看不到的使用不了的

class Animal {  
	public void eat(){ 
	} 
	protected void digest(){ 
	} 
}

这也就理清楚了protected的第一个方面了

再看第二点:基类的 protected 成员是包内可见的,这点先不管他我觉得没什么用,重点是对他的补充,若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法

再看我们开头的代码,是不是就违背了这个规则,所以会报错

public class Person implements Cloneable{
    private int age;
    private String name;

    public static void main(String[] args) {
        Person person = new Person();
        Object clone = person.clone();
    }
}

但是为什么有这个规则,我们还是用上面消化的例子来说

package A;
class Animal {  
	public void eat(){ 
	} 
	protected void digest(){ 
	} 
}

package B
import A.Animal ;
class Cat extends Animal{
	
}

class Rat extends Animal{
	protected void digest(){ 
		super.digest();//调用父类的方法没问题
		Cat cat = new Cat();
		cat.digest()//"digest() has protected access"
	} 
}

其实稍微想一下就知道了,上面代码中,猫和老鼠是平级的,消化能力互不相干,当然不能调用对方的消化功能;

这就是为什么我们在用clone方法的时候不能简单的直接将对象Person.clone()出来的原因了

总之,当B extends A的时候,在子类B的作用范围内,只能调用本子类B定义的对象的protected方法(该方法从父类A中继承而来)。而不能调用其他A类对象的protected 方法

3. 总结

  • 基类的 protected 成员是包内可见的,并且对子类可见;
  • 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法

关于继承

  • 父类的private成员不会被子类继承,子类不能访问。但是子类对象的确包含父类的私有成员。
  • 父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。
  • 父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。
  • 父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样

关于构造函数
构造函数是不能被继承的,但是在子类中一定可以(也必须)借用父类的构造函数
java保证:除了Object类对象,每一个类的实例在构造时,先去调用父类的构造函数。
我们自定义类的构造函数的第一句一定是super(xx,…),如果不是,那么第一句就一定是this(xx,…)去调用本类的另一个构造函数。
如果子类构造函数不显式的调用super(),那么,javac会自动插入super(),也就是父类无参数的构造函数。
对于构造函数,其实类中所有构造函数都是隐式static的。很明显的例证就是 构造函数無需通過实例就可以调用。