一.static:静态
1.static修饰成员变量
static叫静态,可以修饰成员变量,成员方法。
有static修饰的成员变量叫类变量(静态成员变量),随着类的加载而加载,在计算机中只有一份,会被类的所有对象共享
访问方式1:类名.类变量 (推荐)
访问方式2: 对象名.类变量 (不推荐)
实例变量:无static修饰,属于每个对象自己
访问方式:对象名.实例变量
//定义一个学生类
class Student {
//定义1个类变量: 记录学生姓名name
static String name; //共享
//定义1个实例变量, 记录学生的年龄age
int age;
}
//测试类
public class Demo {
public static void main(String[] args) {
//需求1: 访问Student中的类变量name,赋值张三
//方式一
Student.name = "袁华";
//方式二
Student s1 = new Student();
s1.name = "秋雅";
//需求2: 访问Student中的实例变量age,赋值18
s1.age = 18;
//需求3: 获取Student的name和age的值,打印在控制台
System.out.println(Student.name);
System.out.println(s1.age);
Student s2 = new Student();
s2.name = "夏洛";
System.out.println(s1.name); // ?
}
}
总结:类变量既可以使用类名调用,也可以使用对象调用,推荐类名方式
实例变量只能使用对象名调用
2.static修饰成员变量的应用场景
- 类变量的应用场景
在开发中,如果某一个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义为静态变量。
- 注意
访问本类中的类变量,可以省略类名不写
//用户类
class User {
//定义类变量,记录创建用户的数量
static int number;
//编写无参构造,内部对类变量加一
public User() {
number++;
}
}
//测试类
public class Demo {
public static void main(String[] args) {
User u1 = new User();
User u2 = new User();
User u3 = new User();
User u4 = new User();
System.out.println("共创建了" + User.number + "个用户");
}
}
3.static修饰成员方法
类方法:有static修饰的方法,属于类
调用方式:类名.方法名() (推荐)
对象名.方法名() (不推荐)
实例方法:无static修饰的方法,属于对象
调用方式:对象名.方法名()
class Student {
//编写一个类方法(使用Static修饰方法)打印hello
public static void printHello() {
System.out.println("hello~");
}
//编写一个实例方法(不使用Static修饰方法)打印HelloWorld
public void printHelloWorld() {
System.out.println("HelloWorld!");
}
}
public class Demo1 {
public static void main(String[] args) {
//需求1: 类方法调用
//方式一:类名.方法名()
Student.printHello();
//方式二: 先创建对象,对象.方法名()
Student student = new Student();
student.printHello();
//需求2: 实例方法调用
student.printHelloWorld();
}
}
- main方法
java程序的入口方法,被static修饰,是一个静态方法
JVM 在运行类的时候,会自动调用:类名。main(args);
- args
main的形参,jvm在调用main方法的时候是可以给main方法传参数的
public class Demo2 {
public static void main(String[] args){
//比如:我们希望程序延迟运行, 而延迟时间是启动的时候通过参数传递
System.out.println(args.length);
}
}
4.static修饰成员方法的应用场景
类方法最常见的应用场景:做工具类
- 工具类
有时候会开发一些非常实用,具有独立功能的方法,而且希望这些方法可以被重复利用,就像一个个小工具一样,这些方法所在的类,就被称为工具类,而工具类中的方法,习惯称为类方法
- 工具类一般要求
- 工具类中每个方法都可以完成一个独立的功能
- 类中每个方法都要使用static修饰,方便调用者直接调用
- 为防止调用者创建工具类的对象,一般建议将工具类的构造方法私有
- 类名和方法名要求见名知意,类名一般使用xxxUtil
//需求: 单独创建一个工具类,完成求两个整数的最大值、最小值、总和的方法 class MathUtils { private MathUtils() {} //最大值 public static int max(int a, int b) { return a > b ? a : b; } //最小值 public static int min(int a, int b) { return a < b ? a : b; } //总和 public static int sum(int a, int b) { return a + b; } }
public class Demo {
public static void main(String[] args) {
//调用工具类的方法完成功能
int sum = MathUtils.sum(10, 20);
System.out.println(sum);
}
}
5.static的注意事项
- 类方法,实例方法的注意事项
- 类方法中可以直接访问类的成员,不可以直接访问实例成员
- 实例方法中既可以访问类成员,也可以直接访问实例成员
- 实例方法中可以出现this关键字,类方法中不可以出现this关键字
- 总结
- 静态方法只能访问静态内容,实例方法可以访问任何内容
- 静态方法中不能用this
- 本质
静态的内容是跟随类加载而加载的,此时还没对象,也就没有this和实例内容
this:new出来的新对象
public class Demo {
//静态变量
private static int a;
//实例变量
private int b;
//静态方法(类方法)
public static void methodA() {
//只能访问静态成员(静态变量,静态的方法)
System.out.println(a);
//System.out.println(b);
}
//实例方法
public void methodB() {
System.out.println(this);
//实例方法,调用任意内容
System.out.println(a);
System.out.println(b);
methodA();
}
}
6.代码块
代码块是类的5大成分之一(成员变量,构造器,方法,代码块,内部类)
代码块分为两种:
格式 | 特点 | 作用 | |
静态代码块 | static{ } | 类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次 | 完成类的初始化 例如:对类变量的初始化赋值 |
实例代码块 | { } | 每次创建对象时,执行实例代码块,并在构造器之前执行 | 和构造器一样,都是用来完成对象的初始化 例如:对实例变量进行初始化赋值 |
public class Demo {
public static void main(String[] args) {
//类加载
System.out.println(Student.name);
System.out.println(Student.name);
System.out.println(Student.name);
//对象创建
Student s1 = new Student();
Student s2 = new Student();
}
}
class Student {
//静态变量
public static String name;
//实例变量
public int age;
//静态代码块:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次, 可以用来对类变量进行初始化赋值
static {
System.out.println("执行static代码块");
name = "张三";
}
//实例代码块:每次创建对象时,执行实例代码块,并在构造器前执行。
{
System.out.println("执行实例代码块");
age = 18;
}
}
运行结果:
二.继承
1.概述
Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系
class B extends A{
B类:子类(派生类)
A类:父类(基类或者超类)
}
- 特点
- 子类能继承父类的非私有成员变量和方法
- 子类内部可以使用父类的非私有变量和方法
- 继承体系下类对象的创建:子类的对象是由子类和父类共同完成的
public class Demo { public static void main(String[] args) { //继承下对象的创建: 子类的对象是由子类和父类共同完成的 B b = new B(); b.print1(); b.print3(); System.out.println(b.i); } } /* 父类 */ class A { //公有成员变量和方法 public int i; public void print1(){ System.out.println("父类中的print1方法"); } //私有成员变量和方法 private int j; private void print2(){ System.out.println("父类中的print2方法"); } } //使用extends关键字表明继承了A类 class B extends A { private int k; public void print3(){ //父类的非私有的成员变量和方法,子类可以直接使用 System.out.println(i); print1(); //父类私有的成员变量和方法,子类可以继承到, 但是无法使用 //System.out.println(b); //print2(); } }
2.继承的好处
提高代码的复用性(将多个类中的重复代码向上抽取到父类中)
/*
案例需求:
现在需要设计两个实体类,分别是:
教师(Teacher), 属性有: name、age、skill(技能)
学生(Student), 属性有: name、age、hobby(爱好)
请使用继承的思想,设计出类,并创建对象
案例提示:
一共需要设计几个类,有没有相同的属性可以抽取到父类
父类:
Person 属性: name age
子类:
Teacher 属性: skill
Student 属性: score
*/
/**
* 父类,定义所有公共的属性和行为
*/
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* 学生子类,继承Person
*/
public class Student extends Person {
private int score;
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
/**
* 老师的子类,继承Persion父类
*/
public class Teacher extends Person {
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
//测试
public class Demo {
public static void main(String[] args) {
//创建学生对象,设置名字,年龄和分数
Student stu = new Student();
stu.setName("王者荣耀");
stu.setAge(15);
stu.setScore(2);
System.out.println(stu.getName() + "--适合 --" + stu.getAge() + "年龄的小伙伴,评分 : " + stu.getScore());
}
}
3.权限修饰符
用来限制类中的成员(成员变量,成员方法,构造器,代码块....) 能够被访问的范围
种类 | 作用 |
private | 本类 |
缺省 | 本类,本包类 |
protected | 本类,本包类,子孙类 |
private | 任意类 |
public class Demo {
//公开
public void test() {
//在当前类中能方法到四种修饰符修饰的方法
privateMethod();
method();
protectedMethod();
publicMethod();
}
//私有:使用不同的修饰符来修饰方法
private void privateMethod() {
System.out.println("private");
}
//缺省
void method() {
System.out.println("default");
}
//受保护
protected void protectedMethod() {
System.out.println("protected");
}
//公开的
public void publicMethod() {
System.out.println("public");
}
}
public class PackageDemo {
//在同包类中能访问缺省、protected、public修饰的方法
public void test() {
Demo demo = new Demo();
//demo.privateMethod();
demo.method();
demo.protectedMethod();
demo.publicMethod();
}
}
4.单继承
- Java中继承的特点:Java只支持单继承,不支持多继承,但支持多层继承
- Object类是Java中所有类的祖宗类,写的任何一个类,都是Object的子类或子孙类。
- 也就是说:java中所有的类都直接或间接的继承Object(只要类不明确的使用extends继承类,那就默认继承Object类)
public class Demo {
public static void main(String[] args) {
Teacher teacher = new Teacher();
//调用teacher中的方法
teacher.toString();
}
}
//teacher类
class Teacher {
}
5.方法重写
当子类觉得从父类中继承到的某个方法不好用,或者无法满足自己的需求时。子类可以重写一个方法名称,参数列表一样的方法,去覆盖掉继承到的这个方法,这就是方法重写
- 注意:重写后,方法的访问汇遵循就近原则
- 注意事项
- 在重写的方法上标注Override注解,可以指定java编译器,检查方法重写的格式是否正确
- 子类重写父类方法是,访问权限必须大于或者等于父类该方法的权限(public>protected>缺省)
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小
- 私有方法,静态方法不能被重写,如果重写会报错
- 应用场景:重写toString来展示对象中的属性
public class Demo {
public static void main(String[] args) {
Son son = new Son();
son.makeFriend();
}
}
//父类
class Father{
//交朋友
public Father makeFriend(){
System.out.println("正在通过写信的方式交朋友~~~");
return null;
}
}
//子类
class Son extends Father{
//交朋友
public Father makeFriend(){
System.out.println("正在通过写信的方式交朋友~~~");
return null;
}
}
class Student{
private String name;
private int age;
private String course;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", course='" + course + '\'' +
'}';
}
}
public class Demo2 {
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
student.setAge(18);
student.setCourse("Java开发");
//打印对象,本质是调用对象的toString()方法
System.out.println(student); //地址
//System.out.println(student.toString()); //地址
//toString是object类的方法
}
}
6.子类访问成员特点
- 父子类访问成员的顺序
- 在子类方法中访问其他成员(成员变量,成员方法),是依照就近原则 (子类局部-->子类成员-->父类成员 )
- 两个关键字(强行访问子类的成员:this 强行访问父类的成员:super)
//测试
public class Demo {
public static void main(String[] args) {
Son son = new Son();
son.show();
}
}
class Father {
//父类成员
int num = 10;
}
class Son extends Father {
//子类成员
int num = 20;
public void show() {
//子类局部
int num = 30;
//System.out.println(super.num); 使用父类属性
//使用子类的成员变量
System.out.println(this.num);
}
}
7. 子类构造器的特点
子类的全部构造器,都会先调用父类的构造器,在执行自己的
- 子类构造器是如何实现调用父类构造器的:
默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),他会调用父类的无参数构造器,如果父类没有无参数构造器,则必须在子类构造器的第一行手写上super(...),指定去调用父类的有参数构造器
public class Demo {
public static void main(String[] args) {
Son son = new Son();
}
}
//父类和子类
class Father{
private int age;
private String name;
// public Father(){
// System.out.println("Father无参构造器");
// }
public Father(int age, String name) {
System.out.println("Father有参构造器");
this.age = age;
this.name = name;
}
}
class Son extends Father{
private String hobby ;// 爱好
public Son(){
//默认,在构造器第一行,会调用父类的无参构造
//当父类没有无参构造时,子类的构造方法第一行,必须手动调用父类的有参构造
super(18,"张三");
System.out.println("Son无参构造器");
}
}
8.子类构造器应用
经常在子类构造器中调用父类的有参构造器给父类传递参数来完成父类对象的初始化
public class Demo {
public static void main(String[] args) {
//需求: 使用Teacher的带参数构造创建对象,并对三个属性赋值
Teacher teacher = new Teacher("zhangsan",18,"java");
System.out.println(teacher); //地址 teacher.toString
}
}
//父类
class People {
private String name;
private int age;
public People() { }
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
//子类
class Teacher extends People {
private String skill;
public Teacher() { }
public Teacher(String name, int age, String skill) {
super(name, age);
this.skill = skill;
}
@Override
public String toString() {
return "Teacher{" +
"skill='" + skill + '\'' +
"name='" + super.getName() + '\'' +
"age='" + super.getAge() + '\'' +
'}';
}
}
9.this调用兄弟构造器
this()任意类的构造器中,是可以通过this(...)去调用该类的其他构造器的
- 注意事项
this(...),super(...)都只能放在构造器的第一行,因此,有了this(...)就不能写super(...),反之亦然
public class Demo {
public static void main(String[] args) {
//需求: 通过够全参构造创建对象并完成初始化
Student stu1 = new Student("小李", 20, "白马程序员");
System.out.println(stu1);
//需求: 如果用户没有指定学校, ,默认学校为"黑马程序员顺义校区"
Student stu2 = new Student("ikun", 18);
System.out.println(stu2);
}
}
class Student {
private String name;
private int age;
private String school;
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
//调用此构造器,当不传递学校时,默认学校是 "黑马程序员顺义校区"
public Student(String name, int age) {
// this.name = name;
// this.age = age;
// this.school = "黑马程序员顺义校区";
//调用其他的构造器
this(name,age,"黑马程序员顺义校区");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school='" + school + '\'' +
'}';
}
}