研究private、default、protected、public这四个访问控制权限,同时遇到"static、abstract等"也表明是访问修饰符,于是下面一块研究"private、默认(default)、protected、public,及static、final、abstract等"
1、当前的理解
1.1、习惯叫法
定义一个方法,知道格式是
修饰符列表/访问修饰符 返回值类型 方法名(参数列表){ 方法体 }
如
public static void a(){
}
于是认为private及另外三个和static等统称为"修饰符列表/访问修饰符"
1.2之前学习时记下的知识点
private及另外三个称为访问控制权限,控制范围从大到小排序是:public>protected>默认>private
1.3、结合1.1和1.2
汇总前面的说法,得出
1、private、默认(default)、protected、public、static、abstract等,统称为修饰符列表/访问修饰符
2、其中private、默认(default)、protected、public统称访问控制权限
3、将static、abstract等暂时统称为普通修饰符
2、开始研究
2.1、查阅《java语言程序设计》
注:可直接看2.2,已总结好了
得出
1、"访问控制权限"实际规范叫做"可见性修饰符"
2、可见性修饰符作用在类及类的成员上,限制可见的范围
3、不显式使用可见性修饰符则默认是default修饰,这种情况可称为包私有(package-private)或包内访问(package-access)
对前面得出的几点可以再拓展一下
2-实际"类及类的成员"范围很大
类可以引申出枚举、抽象类、接口、注解等直接定义在包中的元素;类的成员包含属性、方法、构造方法、内部类、内部接口等定义在类内的元素
目前知道"类和类的成员":"类"是"类和接口"、"类的成员"是"属性、方法",够用了
看C2类访问C1类。C1类public修饰所以C2类可以访问C1类所以可以实例化C1、 变量x是public修饰所以可以访问到、y是默认(default)修饰,范围是本类同包类,所以同处于一个包(p1)下的C2可以去访问变量y、变量z是private修饰,范围是本类,所以同一个包(p1)下但不同的类(C2)就不能访问到了、m1()是public修饰可以访问到、m2是默认(default)修饰可以访问到、m3是private修饰不能被C2访问->1、关于C2中C1实例点变量z,可能会觉得C1的对象都拿到了,按理实例变量z可以通过对象去点去拿到,实际不行,我觉得原因在于 2、比如A想访问B,B使用了可见性修饰符,实际A也变相限制在了B被限制的范围内才能去访问B。C1的变量z使用private去修饰,限制可见范围是本类,C2中的C1实例因为声明在C2中所以超出了C1本类这个范围,所以不能C1实例去点变量z来访问z
看C3访问C1类。同理,所以简单表述就行
C1是public修饰,所以任意范围内皆可见,所以C3可以访问C1,所以可以实例化C1、x是public修饰故也可以被访问到、y是默认(default)修饰,C3超出"本类、同包类"的范围所以访问不到、z是private修饰,C3超出"本类"范围更加访问不到、m1是public修饰可以被访问到、m2()是默认(default)修饰,C3超出"本类、同包类"范围不可访问到、m3()是private,C3超出"本类"范围访问不到
同理,C1是默认(default)修饰,范围是"本类、同包类",所以处于同一个包(p1)下的C2可以访问到C1、处于不同包(C1在p1包下,C3在p2包下)下的C3不能去访问C1。C2是public修饰,范围任意,所以C3可以访问到C2
(感觉最"默认"的情况是类内可以任意访问)
变量x和方法convert()都是private修饰的,范围是"本类"。当调用私有属性/方法的对象是本类内定义的则可以去调用;当对象不是本类内定义的则不可以去调用。印证了前面所说的,就是x访问y,y限定了可见范围,x想访问y也变相限制自己处于这个限定范围内才可以去访问y
小结
1、"可见性修饰符用于修饰类及类的成员"
2、实际,"private和protected只可以修饰类的成员,也就是只有 'public和default可以修饰类及类的成员' "->或者这么说,"修饰类的可见性修饰符只能是public和默认(default)、修饰类的成员则四个可见性修饰符都可以"
3、再注意,"private和protected不能用于局部变量上"
protected我感觉更像是为了继承而服务的,意思是子类继承父类、因为子类和父类处于不同的包下、所以为了单单这个子类能去访问父类、其他不同包下的类不能访问父类、于是设计了protected
再从上可以比较直观地看出, 可见性修饰符不是乱选的,是限定在合适的范围内最好。因此,1、摒弃"什么都用public修饰"的思路;2、学会根据需求选择使用合适的可见性修饰符
看C2访问C1。C1是public修饰,范围任意,所以可以访问C1,所以可以C1实例化、x是public修饰,同理、y是protect修饰,范围是"本类、同包类、子类",C2C1处于同一个包(p1)中,所以可以访问、z是默认(default)修饰,范围是"本类、同包类", C2C1处于同一个包(p1)中,所以可以访问、u是private修饰,范围是"本类",所以C2不能访问u、m()同y的逻辑
看C3访问C1。C1是public修饰,范围任意,所以可以访问C1,所以可以C1实例化、x是public修饰,同理、y是protect修饰,范围是"本类、同包类、子类",C3C1处于同一个包(p1)中,所以可以访问,也可以说C3是C1的子类,protected修饰的y可以被子类C3访问,此时protected的作用其实不明显,两种说法都可以、z是默认(default)修饰,范围是"本类、同包类", C3C1处于同一个包(p1)中,所以可以访问、u是private修饰,范围是"本类",所以C3不能访问u、m()同y的逻辑
看C4访问C1。C1是public修饰,范围任意,所以可以访问C1,所以可以C1实例化、x是public修饰,同理、y是protect修饰,范围是"本类、同包类、子类",C4C1此时不处于同一个包(p1)中,所以不能说是default起的作用了,只能说是C4是C1的子类,protected修饰的y可以被子类C4访问、z是默认(default)修饰,范围是"本类、同包类", C4C1不处于同一个包(p1)中,所以不可以访问、u是private修饰,范围是"本类",所以C4不能访问u、m()同y的逻辑
看C5访问C1。C1是public修饰,范围任意,所以可以访问C1,所以可以C1实例化、x是public修饰,同理、y是protect修饰,范围是"本类、同包类、子类",C5C1此时超出范围,所以不能访问、z是默认(default)修饰,范围是"本类、同包类", C5C1超出同包类范围,所以不可以访问、u是private修饰,范围是"本类",所以C5不能访问u、m()同y的逻辑
1、"使用protectede修饰符允许任何包中的子类",所以表述protected的可见范围时,应该表述为"本类、同包类、任意包中的子类"更规范"
2、谈到类的功能,我想到的是用于实例化。上面指出第二个大功能是拓展该类来创建该类的子类
知道"方法重写"就知道上面没啥
override是
子类重写要保持返回值类型、方法名、参数列表都相同,再访问权限不能更低可以更高
再要求重写后的方法不能比父类方法抛出更多异常
因此,子类重写父类protected修饰的方法,访问权限可以更高->不能比protected可见性低,
比protected更高的只有public。再当父类的方法是public修饰的,子类重写不能做到更高只能还是
public
2.2、对上一节做一个总结(大致)
1、private、默认(default)、protected、public这四个可统称为"可见性修饰符/访问控制权限/访问控制权限修饰符"(我现在习惯用"可见性修饰符"来称呼)
2、static、final、abstract等目前没整理研究,依旧将这一类暂时称为"普通修饰符"(我暂时用"普通修饰符"来称呼)
3、12统称为"修饰符列表"
4、可见性修饰符作用在"类和类的成员"上
可见性修饰符可修饰在类或类的成员上,限制其可被访问的范围。类可以引申出类、接口这些、类的成员可引申出属性、方法这些
各个可见性修饰符的可见范围需要记住:private限制可见范围是本类,限制本类内可见及本类内可以去访问、默认(default)限制可见范围是本类、同包类,只有处于这个范围内的才可以去访问、protected限制本类、同包类、任意包下的子类的范围内可见,处于这个范围内的可去访问、public任意范围内都可以去访问
5、尽管"可见性修饰符作用在 '类和类的成员' 上",但实际
只有public和default可以修饰类及类的成员、private和protected只可以修饰类的成员(换句话说,修饰类的可见性修饰符只可以是public和默认(default),修饰类的成员则四个可见性修饰符都可以)
再注意两点特殊:private和protected不能修饰局部变量。接口中的方法、属性,都是public修饰的
2.3、继续研究整理
这块没啥,可不看
介绍访问控制修饰符
访问控制符是一组限定类、属性或方法是否可以被程序里的其他部分访问和调用的修饰符。类的访问控制符只能是空或者 public,方法和属性的访问控制符有 4 个,分别是 public、 private、protected 和 friendly。其中 friendly 是一种没有定义专门的访问控制符的默认情况->可知,空是friendly,就是对应的默认(default)
访问控制修饰符的权限如下表
访问范围 | private | friendly(默认) | protected | public |
---|---|---|---|---|
同一个类 | 可访问 | 可访问 | 可访问 | 可访问 |
同一包中的其他类 | 不可访问 | 可访问 | 可访问 | 可访问 |
不同包中的子类 | 不可访问 | 不可访问 | 可访问 | 可访问 |
不同包中的非子类 | 不可访问 | 不可访问 | 不可访问 | 可访问 |
可见friendly除了名和default不同外,效果一致
访问控制在面向对象技术中处于很重要的地位。合理地使用访问控制符可以通过降低类和类之间的耦合性(关联性)来降低整个项目的复杂度,也便于整个项目的开发和维护
在 Java 语言中,访问控制修饰符有 4 种
1. private
用 private 修饰的类成员只能被该类自身的方法访问和修改,而不能被任何其他类(包括该类的子类)访问和引用。因此private 修饰符具有最高的保护级别
一般大多数的成员变量都是修饰符为private的,它们不希望被其他任何外部的类访问。对private修饰的成员变量的进行获取和更改,一般结合get(),set() 这些public修饰的 方法,这实现了Java面向对象的封装思想
2. friendly(默认)
如果一个类没有访问控制符,即没有显式声明访问修饰符,说明它具有默认的访问控制特性。这种默认的访问控制权规定:该类只能被同一个包中的类访问和引用,而不能被其他包中的类使用,即使其他包中有该类的子类。(如果这个子类也是同包的,则可以访问)这种访问特性又称为包访问性(package private)
同样,类内的成员如果没有访问控制符,也说明它们具有包访问性,或称为友元(friend)。它是针对本包访问而设计的,定义在同一个文件夹中的所有类属于一个包,任何处于本包下的类、接口、异常等都可以相互访问,所以前面的程序要把用户自定义的类放在同一个文件夹中(Java 项目默认的包),以便不加修饰符也能运行
3. protected
用保护访问控制符 protected 修饰的类成员可以被三种类所访问:该类自身、与它在同一个包中的其他类以及在其他包中的该类的子类。使用 protected 修饰符的主要作用是允许其他包中它的子类来访问父类的特定属性和方法,否则可以使用默认访问控制符->所以protected主要的作用就是用来保护子类的。它的含义在于子类可以用它修饰的成员,其他的不可以。它相当于传递给子类的一种继承的东西
4. public
当一个类被声明为 public 时,它就具有了被其他包中的类访问的可能性。 如果尝试访问的公共类位于不同的包中,则仍需要导入公共类。只要包中的其他类在程序中使用 import 语句引入 public 类 就可以访问和引用这个类
类中被设定为 public 的方法是这个类对外的接口部分,避免了程序的其他部分直接去操作类内的数据,实际就是数据封装思想的体现。每个 Java 程序的主类都必须是 public 类,也是基于相同的原因
应用程序的main()
必须声明为public
,否则Java解释器无法调用它来运行该类
注意
Java的访问控制是停留在编译层的,也就是它不会在.class文件中留下任何的痕迹,只在编译的时候进行访问控制的检查。其实通过反射的手段是可以访问任何包下任何类中的成员,例如访问类的私有成员也是可能的
private的一个典型使用场景是单例模式,将构造函数声明为private
public class Singleton {
/**
* 将构造函数声明为private,不允许外部类在使用时直接通过构造函数进行实例化
*/
private Singleton() {
}
/**
* 单例必须通过该方法获取
*/
public static Singleton getInstance() {
return InstanceWrapper.INSTANCE;
}
private static class InstanceWrapper {
static final Singleton INSTANCE = new Singleton();
}
}
2.4、对前面(主要是可见性修饰符)进行一点补充及研究整理普通修饰符
2.4.1、"修饰符列表"也可叫做"作用域修饰符"
作用域修饰符可分为访问修饰符和非访问修饰符。访问修饰符就是指的可见性修饰符,非访问修饰符是指的static、final、abstract这些
2.4.2、"类和类的成员"的引申情况
前面说过,知道"类可以引申出类和接口;类的成员可以引申出属性、方法"足够了
下面看引申出的别的情况
注:前面在说的"类"其实都在说"外部类"。如果是内部类,下面开始会特别说明
类引申出接口,接口的情况
接口修饰符
public:所有包可见
default:(缺省)同一个包中可见 ->符合"只有public和default会修饰类"
strictfp:(java关键字) 即 strict float point (精确浮点) ->了解
接口中支持三种方法
即无消息体的方法(默认修饰符是public abstract)、通过default保留字定义的方法(默认修饰符是public)、通过static保留字定义的方法(默认修饰符是public)
类的成员引申出内部类,内部类的情况
内部类是定义在类里面的类,外部类是相对于内部类而言的。内部类是和属性、方法一个级别的类内定义的元素。所以前面准确的表述是,"外部类只能public和default去修饰"、再"类的成员则四个可见性修饰符都可以"在这里则可以说"四个可见性修饰符都可以去修饰内部类"
问:类分外部类和内部类,他们的访问控制是相同的吗?
答,内部类作为外部类的成员,可以四个可见性修饰符都去修饰内部类。外部类只能是public和默认(default)去修饰
内部类可以无条件访问外部类的所有成员属性和成员方法,包括private成员和静态成员
内部类又分:实例内部类、静态内部类、局部内部类
->匿名内部类是局部内部类的一种,所以上面分三种而不是四种。再因为内部类和属性、方法是一个级别的,都是类内定义的元素,所以可以按照属性的逻辑去分析内部类。也就是,实例内部类看作实例变量、静态内部类看作静态变量、局部变量看作局部变量去理解
静态内部类:使用static修饰的内部类
实例内部类:作为外部类的一个成员存在,与外部类的属性、方法并列
局部内部类:定义在外部类的方法体里面的类
匿名内部类:特殊的内部类,是局部内部类的一种,该类没有名字(局部内部类基本不用,很少)
实例内部类修饰符除了四个可见性修饰符外,还有
abstract
final
static:可以当做普通类使用而不用先实例化一个外部类
strictfp:(java关键字) 即 strict float point (精确浮点)(可修饰类、接口、方法)
局部内部类
局部类有一个优势,即对外部世界完全隐藏。即使是同一个类中其他的方法也不能访问它
局部类可以访问包含它的外部类,同时也可以访问局部变量
局部类不能用public或private或protected可见性修饰符去进行声明,他的作用域被限定在这个方法中
外部类的修饰符除了public和默认(default)外,还有
abstract:表示是抽象类
final:表示类不可以被继承
scrictpf:(java关键字) 当对一个类或接口使用 strictfp 关键字时,该类中的所有代码,包括嵌套类型中的初始设定值和代码都将严格地进行计算。严格约束意味着所有表达式的结果都必须是 IEEE 754 算法对操作数预期的结果,以单精度和双精度格式表示
为什么不能使用private和protected修饰外部类?
我们使用访问修饰符去修饰类,就是为了让类之间根据各种权限来访问。
假如外部类使用private修饰,则不能被其它类所访问,这个类也就失去了意义。
假如外部类使用protected修饰,与default相比,在包内可访问的基础上,包外的子类也可访问。但包外想成为子类需要先继承父类,然而无法找到该父类实际上无法继承(先有鸡还是先有蛋的问题),效果与default一致,也就没必要存在了
2.4.3、修饰符列表中常用的普通修饰符:static、final、abstract、transient 、volatile...
注:关于可见性修饰符,我们知道可以使用在什么位置上及起到了什么作用(限定了多大的范围。普通修饰符同理,也是需要知道可使用在什么位置上及起了什么作用。下面关于普通修饰符的使用,重点看小结部分即可
static
使用在: 类(指内部类)、变量、方法、初始化函数 ->static修饰的初始化函数是指的静态代码块(static初始化函数(static initializer),是一段在加载类时会执行的程序代码。它会在其他程序可以使用该类之前就执行。具体是static初始化函数是由类调用的。类调用时先执行static初始化函数,然后才执行主函数的)
static 修饰符,修饰变量,称为类变量或静态变量。修饰方法,成为类方法或静态方法。静态变量是和类存在一起的,每个实例共享这个静态变量,在类加载时初始化。static修饰类只能修饰内部类,用他修饰后就成了静态内部类。静态内部类可以当做普通类使用而不用先实例化一个外部类
静态变量和实例变量的区别
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)
对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)
所以一般在需要实现以下两个功能时使用静态变量
- 在对象之间共享值时
- 方便访问变量时
静态方法
-
静态方法可以直接通过类名调用,任何的实例也都可以调用静态方法。静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!就是java面向对象的思想,实例是你这个类本身的属性,你会用这个本身的属性去做一些事情,而这些事情不是固定的,不能像静态方法一样一成不变
-
由于static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。静态方法是类内部的一类特殊方法,只有在需要时才将对应的方法声明成静态的,一个类内部的方法一般都是非静态的
-
有时你希望定义一个类成员,使它的使用完全独立于该类的任何对象。通常情况下,类成员必须通过它的类的对象访问,但是可以创建这样一个成员,它能够被它自己使用,而不必引用特定的实例。在成员的声明前面加上关键字static(静态的)就能创建这样的成员。如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象。你可以将方法和变量都声明为static。static 成员的最常见的例子是main( ) 。因为在程序开始执行时必须调用main() ,所以它被声明为static
-
声明为static的方法有以下几条限制
- 它们仅能调用其他的static 方法
- 它们只能访问static数据
- 它们不能以任何方式引用this 或super
小结
static可使用在内部类(只能是内部类,不能是外部类)、变量、方法、初始化函数上。修饰内部类使其称为静态内部类、修饰变量使其称为静态变量、修饰方法使其称为静态方法、修饰初始化函数使其成为静态代码块
final
使用在:类、变量、方法
使用final关键字,一经声明便不可继承、不可修改和不能覆盖。具体是final修饰的类不能够被继承、修饰的方法不能被继承的子类重新定义(方法不能被重写)、修饰的变量为常量,表示是最终的变量,是不可修改的
关于这个常量,要求被声明为final的变量必须在声明时给定初值,在以后的引用中只能读取不能更改。其实建议记忆为"只能赋一次值,定义时必须赋值且后面不能再赋值"更合适。再常量的数据类型可以有两种,final修饰基本类型表示基本类型赋值以后不能再被赋值、final修饰对象表示这个属性不能再指向其他对象(引用不变),但是他指向的这个对象本身还是可以被改变的当变量是引用类型时,意思是"被声明为 final 的对象的引用不能指向不同的对象,但是 final 对象里的数据可以被改变"。举例,比如一个Student实例,用final修饰,指向Student实例不会变,但是Student实例的属性,比如name、age这些的值可以变
编译器在调用final方法时会转入内嵌机制(直接将方法主体插入到调用处),大大提高执行效率(50%左右)。API类中的许多方法,如Object类中的getClass方法为final方法。同时,final还不能用于修饰构造方法
特殊情况
class Base {
private final void method() {
System.out.println(“In Base…”);
}
}
class Sub extends Base {
public void method() {
System.out.println(“In Sub…”);
}
}
虽然父类和子类中都有method这个方法,但是不算是重写,因为基类中的method()的访问修饰符是private,表示这个方法是私有的、对子类是不可见的,所以子类中的method()其实是一个全新的方法,不是对基类方法的重写,仅仅同名罢了
final 修饰符通常和 static 修饰符一起使用来创建类常量
public class Test{
final int value = 10;
public static final int BOXWIDTH = 6;
static final String TITLE = "Manager";
public void changeValue(){
value = 12; 将输出一个错误
}
}
上面changeValue()对value变量再赋值,不行,原因就在于final修饰的变量初始化时赋值且只能赋值一次,所以再改改不了
关于final变量,还需要知道
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。final变量定义的时候可以先声明而不给初值。这种变量也称为final空白。无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是final空白在final关键字final的使用上提供了更大的灵活性。为此,一个类中的final数据成员就可以实现依对象而有所不同却有保持其恒定不变的特征
来看下final以及final static修饰的变量的初始化方式
-----------------成员变量------------------
初始化方式一,在定义变量时直接赋值
private final int i = 3;
初始化方式二,声明完变量后在构造方法中为其赋值
如果采用用这种方式,那么每个构造方法中都要有j赋值的语句
private final int j;
public FinalTest() {
j = 3;
}
如果取消下面对构造方法的注释,程序就会报错,因此它没有为j赋值
/*public FinalTest1(String str) {
}*/
为了方便我们可以这样
public FinalTest(String str) {
this(); 调用无参构造器
}
如果是下面的代码写法则会报错,因为对j重复赋值
/*public FinalTest1(String str1, String str2) {
this();
j = 3;
}*/
初始化方式三,声明完变量后在构造代码块中为其赋值
如果采用此方式就不能在构造方法中再次为其赋值
构造代码块中的代码会在构造函数之前执行,如果在构造函数中再次赋值
就会造成final变量的重复赋值
private final int k;
{
k = 4;
}
-----------------类变量(静态变量)------------------
初始化方式一,在定义类变量时直接赋值
public final static int p = 3;
初始化方式二,在静态代码块中赋值
成员变量可以在构造函数中赋值,但是静态变量却不可以
因为成员变量属于对象独有,每个对象创建时只会调用一次构造函数,
因此可以保证该成员变量只被初始化一次
而静态变量是该类的所有对象共有,每个对象创建时都会对该变量赋值
这样就会造成变量的重复赋值
public final static int q;
static {
q = 3;
}
再看final方法
类中的 final 方法可以被子类继承,但是不能被子类修改。声明 final 方法的主要目的是防止该方法的内容被修改。如下所示,使用 final 修饰符声明方法
public class Test{
public final void changeName(){
// 方法体
}
}
再看final类
final 类不能被继承,没有类能够继承 final 类的任何特性
public final class Test {
// 类体
}
小结
final可以修饰类、变量、方法。修饰类使其不能被继承、修饰方法使其不能被重写、修饰变量使其成为常量,"只能赋一次值,定义时必须赋值且后面不能再赋值",常量分数据类型:基本数据类型的常量没什么要改的,引用类型的常量则引用还是不能再赋值,但是引用指向的对象,对象的属性可以更改
abstract
使用在:类、方法
abstract修饰符,用来创建抽象类和抽象方法。抽象类不能创建实例,抽象方法没有方法体
看抽象类
声明抽象类的唯一目的是为了将来对该类进行扩充。一个类不能同时被abstract 和 final 修饰。抽象类可以不包含抽象方法,如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
abstract class Caravan{
private double price;
private String model;
private String year;
public abstract void goFast();
public abstract void changeColor();
}
如上的goFast()和changeColor()就是抽象方法
抽象方法是一种没有任何实现的方法,抽象方法的声明以分号结尾,该方法的的具体实现由子类提供。抽象方法不能被声明成 final 和 static。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类
public abstract class SuperClass{
abstract void m(); 抽象方法
}
class SubClass extends SuperClass{
实现抽象方法
void m(){
.........
}
}
小结
final可以修饰类和方法。修饰类使其成为抽象类,抽象类不能被继承,无构造方法就不能创建实例。修饰方法使其成为抽象方法,抽象方法无方法体,方法签名后直接跟分号而不是大括号了
(摘自《java语言程序设计》)
关于普通修饰符的研究整理,差了点,记住static、final、abstarct这三个常见的也行
再下图了解即可
transient ->以下的普通修饰符了解即可
告诉编译器,在类对象序列化的时候,此变量不需要持久保存
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型
public transient int limit = 55; 不会持久化
public int b; 持久化
当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复
定义一个需要序列化的类
class People implements Serializable{
String name;
transient Integer age;
public People(String name,int age){
this.name = name;
this.age = age; }
public String toString(){
return "姓名 = "+name+" ,年龄 = "+age; }
}
public class TransientPeople {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
People a = new People("李雷",30);
System.out.println(a); 打印对象的值
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d://people.txt"));
os.writeObject(a); 写入文件(序列化)
os.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream("d://people.txt"));
a = (People)is.readObject(); 将文件数据转换为对象(反序列化)
System.out.println(a); 年龄 数据未定义
is.close();
}
}
运行结果如下
姓名 = 李雷 ,年龄 = 30
姓名 = 李雷 ,年龄 = null
synchronized
synchronized 关键字声明的方法同一时间只能被一个线程访问
synchronized 修饰符可以应用于四个访问修饰符
public synchronized void showDetails(){
.......
}
synchronized 和 volatile修饰符,主要用于线程的编程
volatile
指出可能有多个线程修改此变量,要求编译器优化以保证对此变量的修改能够被正确处理
volatile修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。一个volatile 对象引用可能是 null
public class MyRunnable implements Runnable{
private volatile boolean active;
public void run(){
active = true;
while (active){ 第一行
代码
}
}
public void stop(){
active = false; 第二行
}
}
通常情况下,线程A调用 run() (在 Runnable 开启的线程),线程B调用 stop() 。如果 第一行 中缓冲区的 active 值被使用,那么在第二行 的 active 值为 false 时循环不会停止。但是以上代码中我们使用了 volatile 修饰 active,所以该循环会停止
volatile可以用在任何变量前面,但不能用于final变量前面。因为final型的变量是禁止修改的。使用场景之一:单例模式中采用DCL双锁检测(double checked locking)机制。在多线程访问的情况下可使用volatitle修改来保证多线程下的可见性。缺点是性能有损失,因此单线程情况下不必用此修饰符
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
native
用该修饰符定义的方法在类中没有实现,而大多数情况下该方法的实现是用C、C++编写的
synchronized:修饰方法,多线程的支持
2.4.4、关于protected的案例
已知,protected限定可见范围是"本类、同包类、任意包下的子类"
实际
//在ch13Test包中定义父类Animal,包含一个protected权限的成员变量 i 和成员方法eat()
package ch13Test;
public class Animal {
protected int i = 5;
protected void eat() {
System.out.println("Animals eat.");
}
}
//情况一:子类Cat定义在ch13包中,在子类中直接使用Animal的protected成员变量和方法
package ch13;
import ch13Test.Animal;
public class Cat extends Animal{
public void eat() {
i = 7;
eat();
System.out.println("Cat is eating" + i);
}
} //可以访问到
//情况二:子类Cat定义在ch13包中,在子类中通过子类的对象访问Animal的protected成员变量和方法
package ch13;
import ch13Test.Animal;
public class Cat extends Animal{
public void show() {
Cat cat = new Cat();
cat.i = 10;
cat.eat();
System.out.println("Cat is eating" + cat.i);
}
} //可以访问到
//情况三:子类Cat定义在ch13包中,在子类中通过父类的对象访问父类Animal的protected成员变量和方法
package ch13;
import ch13Test.Animal;
public class Cat extends Animal{
public void show() {
Animal animal = new Animal();
// 无法通过父类对象访问父类的protected成员
// animal.i = 9;
// animal.eat();
// System.out.println("Cat is eating" + animal.i);
}
} //访问不到
//情况四:子类Cat和Dog都定义在ch13包中,在Dog子类中通过其他子类(Cat)的对象访问父类
//Animal的protected成员变量和方法
package ch13;
import ch13Test.Animal;
public class Dog extends Animal{
public void show() {
Cat cat = new Cat();
// 无法通过其他子类对象访问父类的protected成员
// cat.i = 9;
// cat.eat();
}
} //访问不到
//情况五:子类Cat定义在ch13包中,同包中其他类中通过子类的对象访问父类Animal的
//protected成员变量和方法
package ch13;
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
// cat.i = 9;
// cat.eat();
}
} //访问不到
[文章出处] ,给出的总结是
1. 子类可以通过继承获得不同包父类的protected权限成员变量和成员方法,在子类中可以直接访问
2. 在子类中可以通过子类的对象访问父类的protected成员变量和方法
3. 在子类中反而不能通过父类的对象访问父类的protected成员变量和方法
4. 在子类中不能通过其他子类的对象访问父类的protected成员变量和方法
5. 在与子类同包的其他类中不能通过子类的对象访问父类的protected成员变量和方法
->用前面的结论不能解释,后期再说了