权限修饰符练习
创建a包,在a包中创建A类,提供方法
public class A {
protected void m(){
}
}
创建b包,在b包中创建B类,B类继承A类
public class B extends A {
}
在b包中再创建C类,提供main方法
public class C {
public static void main(String[] args) {
B b = new B();
/**
* m方法是使用protected修饰的
* 所以使用范围是本类中,子类中和同包类中
* m方法目前不在A类这个本类中用
* m方法目前没有在子类B中调用,因为现在在C类中
* m方法目前在同包类中吗? 不在
*/
// b.m();
}
}
在b包中创建D类,继承A类,提供main方法
public class D extends A {
public static void main(String[] args) {
B b = new B();
b.m();
}
}
1 static
static是java中的关键字,可以修饰数据,方法,代码块,内部类
1.1 静态变量
static 修饰的数据称之为静态变量,或者叫类变量。静态变量是随着类的加载而加载到方法区,在方法区中被赋予了默认值。静态变量是先于对象而存在的,可以通过类名来调用。该类产生的所有的对象共用的是同一个静态变量。
每一个对象存储的都是该静态变量的地址。
使用格式: 类名.静态变量
类是加载到方法区的
类只加载一次
类会在首次使用时加载到方法区
静态变量的使用
public class TestDemo {
public static void main(String[] args) {
Person person1 = new Person();
person1.name = "李小龙";
person1.age = 31;
// person1.kongfu = "截拳道";
Person.kongfu = "截拳道";
Person person2 = new Person();
person2.name = "叶问";
person2.age = 40;
// person2.kongfu = "咏春";
Person.kongfu = "咏春";
// 静态变量在多个对象之间是共享的,是属于类的,而不是属于对象的
// 静态变量可以使用 类名.静态变量名 来调用
System.out.println(person1);
System.out.println(person2);
}
}
class Person{
// 姓名
String name;
// 年龄
int age;
// 功夫
static String kongfu;// 静态变量
public String toString(){
return name + "\t" + age + "\t" + kongfu;
}
}
静态变量的内存图
思考:
静态变量能不能定义在构造方法中?
不能。静态变量在类加载
的时候加载并且初始化,构造方法在创建对象的时候执行。静态变量是存储在方法区,构造方法在栈内存中执行,创建的对象在堆内存中。
静态变量能不能定义在成员方法中?
不能,道理同上。
1.2 静态方法
static 修饰的方法称之为静态方法。静态方法在类加载时加载到方法区,并没有执行只是存储在方法区。在方法被调用的时候去栈内存中执行。静态方法本身也是先与对象而存在。习惯上是通过类名来调用静态方法。
格式: 类名.静态方法名();
静态方法一般用在工具类中。
public class SmsUtil {
public static void sendSms(){
System.out.println("发送短信...");
}
}
// 调用方式
SmsUtil.sendSms();
思考:
静态方法中能使用this/super吗? 不能
静态方法中能够使用本类中的非静态方法? 不能。
静态方法能否被继承? 能
public class TestDemo3 {
public static void main(String[] args) {
// 向上转型
Student.eat();
}
}
class People{
public static void eat(){
System.out.println("人要吃饭");
}
}
class Student extends People{
}
静态方法能够被重写? 不能
父子类中可以存在方法签名一直的静态方法,称之为隐藏(hide)。
隐藏也适用于重写的五个原则
public class TestDemo3 {
public static void main(String[] args) {
// 向上转型
People people = new Student();
people.eat();
}
}
class People{
public static void eat(){
System.out.println("人要吃饭");
}
}
class Student extends People{
public static void eat(){
System.out.println("学生要吃饭");
}
}
练习:定义一个类,统计这个类所创建对象的个数。
public class Demo {
static int i = 0;
{
i++;
}
}
1.3 静态代码块
- 静态代码块就是使用static修饰的代码块,只在类加载的时候执行一次
class Person {
static {
// 静态代码块 是在类加载的时候执行一次
System.out.println("静态代码块执行了");
}
}
- 只要使用到了类,这个类的静态代码块就会被调用(无论是创建对象还是调用静态方法或者调用静态变量)
new Person();
Person.i = 10;
Person.method();
面试题:静态代码块、构造代码块、构造方法的执行顺序问题
- 第一种情况
public class demo3 {
public static void main(String[] args) {
// 创建子类
// 当要创建SB对象时,需要先加载SB这个类。
// 加载SB类时,先要加载SB的父类SA。
// SA加载时会调用它的静态代码块,所以第一个是A1
// 再去加载SB类,会执行SB的静态代码块B1
// 然后调用SB()方法,这个方法中有一个隐藏的super()
// 这时又去调用父类的SA(),再调用SA()方法之前会先调用SA的构造代码块A2
// 然后调用SA,打印A3
// 然后SB()继续执行,但是发现有构造代码块,所以先执行SB的构造代码块B2
// 最后SB()继续执行,打印B3
new SB(); // A1 B1 A2 A3 B2 B3
}
}
// 静态代码块、构造代码块、构造方法的执行顺序问题
class SA{
static {
// 静态代码块 类加载时执行一次
System.out.println("A1");
}
{
// 每次创建对象时,都会在构造方法之前执行
System.out.println("A2");
}
public SA(){
// 每次创建对象时执行
System.out.println("A3");
}
}
class SB extends SA{
static {
System.out.println("B1");
}
{
System.out.println("B2");
}
public SB(){
System.out.println("B3");
}
}
- 第二种情况
public class demo3 {
public static void main(String[] args) {
// 创建子类
// 由于在加载SB类时需要创建一个静态变量SC,会先执行SC的构造方法,所以A1后面跟上C
new SB();// A1 C B1 A2 A3 B2 B3
}
}
// 静态代码块、构造代码块、构造方法的执行顺序问题
class SA{
static {
// 静态代码块 类加载时执行一次
System.out.println("A1");
}
{
// 每次创建对象时,都会在构造方法之前执行
System.out.println("A2");
}
public SA(){
// 每次创建对象时执行
System.out.println("A3");
}
}
class SB extends SA{
static SC c = new SC();
static {
System.out.println("B1");
}
{
System.out.println("B2");
}
public SB(){
System.out.println("B3");
}
}
class SC{
public SC(){
System.out.println("C");
}
}
- 第三种情况
public class demo3 {
public static void main(String[] args) {
// 创建子类
// 由于SD只有声明,根本没有创建,所以构造方法不会执行
new SB();// A1 C B1 A2 A3 B2 B3
}
}
// 静态代码块、构造代码块、构造方法的执行顺序问题
class SA{
SD d;
static {
// 静态代码块 类加载时执行一次
System.out.println("A1");
}
{
// 每次创建对象时,都会在构造方法之前执行
System.out.println("A2");
}
public SA(){
// 每次创建对象时执行
System.out.println("A3");
}
}
class SB extends SA{
static SC c = new SC();
static {
System.out.println("B1");
}
{
System.out.println("B2");
}
public SB(){
System.out.println("B3");
}
}
class SC{
public SC(){
System.out.println("C");
}
}
class SD extends SC{
public SD(){
System.out.println("D");
}
}
- 第四种情况
public class demo3 {
public static void main(String[] args) {
// 创建子类
// A2打印后,还需要创建SD。
// 由于SD是SC的子类,所以要先执行SC(),再执行SC()
// 所以在A2后面添加了C D
new SB();// A1 C B1 A2 C D A3 B2 B3
}
}
// 静态代码块、构造代码块、构造方法的执行顺序问题
class SA{
SD d;
static {
// 静态代码块 类加载时执行一次
System.out.println("A1");
}
{
// 每次创建对象时,都会在构造方法之前执行
System.out.println("A2");
d = new SD();
}
public SA(){
// 每次创建对象时执行
System.out.println("A3");
}
}
class SB extends SA{
static SC c = new SC();
static {
System.out.println("B1");
}
{
System.out.println("B2");
}
public SB(){
System.out.println("B3");
}
}
class SC{
public SC(){
System.out.println("C");
}
}
class SD extends SC{
public SD(){
System.out.println("D");
}
}