Java面向对象高级特性有以下几个:继承,多态,封装,抽象,接口,匿名内部类,静态成员,final关键字,异常处理等。
我会将它分为三章详细讲,本章主要讲静态和继承
static
static叫静态,可以修饰成员变量、成员方法。
类变量(静态成员变量):有static修饰,属于类,在计算机里只有一份,会被类的全部对象共享。
实例变量(对象变量):无static修饰,属于每个对象的。
package itchinajie.d1_static_demo;
public class Test {
public static void main(String[] args) {
//目标:掌握有无static修饰成员变量的用法,特点
//1、类变量的用法
//类名.类变量(推荐)
Student.name = "张三";
//对象.类变量(不推荐)
Student s1 = new Student();
s1.name = "马冬梅";
Student s2 = new Student();
s2.name = "秋雅";
System.out.println(s1.name);//秋雅
System.out.println(Student.name);//秋雅
//2、实例变量的用法,属于每个对象的变量
//对象.实例变量
s1.age = 23;
s2.age = 18;
System.out.println(s1.age);
System.out.println(s2.age);
//System.out.println(Student.age);
}
}
package itchinajie.d1_static_demo;
public class Student {
//类变量(静态修饰变量)
static String name;
//实例变量(对象变量)
int age;
}
类变量的应用场景:
在开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来记住。
系统启动后,要求用户类可以记住自己创建了多少个用户对象了。
package itchinajie.d1_static_demo;
public class User {
//类变量
public static int number;
public User(){
//User.number ++;
//注意:在同一个类中,访问自己的类的类变量,才可以省略类名不写
number++;
}
}
package itchinajie.d1_static_demo;
public class Test2 {
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);
}
}
类变量只需要一份,且需要时被共享(访问或修改)
实例变量是每个对象都要有一份,数据各不同(如:name、sorce、age)
static修饰成员方法
类方法(static修饰成员方法):
类方法是指属于类本身而不是类的实例的方法。在Java中,类方法通常使用关键字`static`进行修饰,可以直接通过类名来调用,而不需要实例化该类的对象。类方法被称为静态方法,因为它们在内存中只有一份副本,而不是每个实例都有自己的副本。类方法通常用于执行与整个类相关的操作,例如工具类中的常用方法或者程序的入口方法main。
实例方法(无static修饰成员方法):
实例方法是指属于类的实例(对象)而不是类本身的方法。在Java中,实例方法不使用`static`关键字修饰,必须通过类的实例来调用。每个类的实例都有自己的实例方法,它们可以访问和操作实例的属性,并且可以调用其他实例方法。实例方法通常用于描述对象的行为和操作,并且会随着每个对象的创建而存在不同的副本。
package itchinajie.d2_static_method;
public class Student {
double score;
//类方法
public static void printHelloWorld(){
System.out.println("Hello World");
System.out.println("Hello World");
}
//实例方法(对象方法)
public void printPass(){
System.out.println("成绩:" +
(score >= 60 ? "及格" : "不及格"));
}
}
package itchinajie.d2_static_method;
public class Test {
public static void main(String[] args) {
//目标:掌握用static修饰方法的用法
//1、类方法的用法
//类名.类方法(推荐)
Student.printHelloWorld();
//对象.类方法(不推荐)
Student s = new Student();
s.printHelloWorld();
//2、实例方法的用法
//对象.实例方法
s.printPass();
//Student.printPass();//报错
}
}
在这里不得不提一嘴,main方法,我们之前常用的main方法能直接执行其他方法,到底为啥呢?
main方法能直接执行是因为在Java虚拟机(JVM)中,程序在运行时会自动查找并调用名为main的方法作为程序的入口点。当你运行一个Java程序时,JVM会寻找包含main方法的类并执行该方法。因此,main方法就成为了程序的入口,用户可以通过调用这个方法来启动整个程序的执行。
main方法是Java程序的入口方法,是程序执行的起点。在Java程序中,main方法是一个特殊的静态方法,格式为: ```java public static void main(String[] args) ```
类方法和实例方法的区别:
Java中的类方法和实例方法是面向对象编程中的两种基本方法类型,它们在方法和对象的关系、调用方式、访问权限等方面存在差异。
1. **定义和归属**:
- **类方法**(静态方法):使用`static`关键字修饰的方法。类方法属于类本身,而不是类的某个对象。即使没有创建类的对象,类方法也可以直接通过类名调用。
- **实例方法**:没有使用`static`关键字修饰的方法。实例方法属于类的对象,只能通过类的对象来调用。
2. **调用方式**:
- **类方法**:可以直接通过类名调用,也可以通过类的对象调用。
- **实例方法**:必须通过类的对象来调用。
3. **访问权限**:
- **类方法**:由于类方法属于类本身,因此它们可以访问类的静态成员变量和方法。
- **实例方法**:可以访问类的实例成员变量和方法,也可以访问类的静态成员变量和方法。
4. **对实例变量和类变量的访问**:
- **类方法**:由于类方法不属于任何对象,它们不能直接访问实例变量。但可以访问类变量(变量)。
- **实例方法**:可以访问类的实例变量和类变量。
5. **使用`super`和`this`关键字**:
- **类方法**:由于类方法不依赖于类的任何对象,所以它们不能使用`super`和`this`关键字,因为这些关键字分别用于引用父类和当前类的对象。
- **实例方法**:可以使用`super`和`this`关键字。
6. **内存分配和多态性**:
- **类方法**:类的所有对象共享类方法的方法体,因此类方法在程序运行期间只被加载一次,并且其方法体不会因为创建多个对象而重复执行。
- **实例方法**:每个对象都有自己的方法体副本,因此实例方法可以独立于其他对象进行操作。 这些区别体现了Java在方法设计上的灵活性和面向对象编程的原则。在实际开发中,应根据需要选择使用类方法还是实例方法。
static的注意事项
package itchinajie.d4_static_attention;
public class Student {
static String schoolName;//类变量
double score;//实例变量
//1、类方法中可以直接访问类的成员,不可以直接访问实例成员
public static void printHelloWorld(){
//注意:同一个类中,访问类成员,可以直接省略类名不写
schoolName = "黑马";
printHelloWorld2();
//printPass();//报错
// System.out.println(score);//报错
}
//类方法
public static void printHelloWorld2(){
}
//2、实例方法中既可以访问类成员也可以直接访问实例成员
//实例方法
//3、实例方法中可以出现this关键字,类方法中不能出现
public void printPass(){
schoolName = "黑马";
printHelloWorld2();
System.out.println(score);
printPass();
System.out.println(this);
}
//实例方法
public void printPass2(){
}
}
1、类方法中可以直接访问类的成员,不可以直接访问实例成员。
2、实例方法中既可以访问类成员也可以直接访问实例成员。
3、实例方法中可以出现this关键字,类方法中不可以出现this关键字。
static的应用知识:代码块(源码用的,认识即可)
package itchinajie.d5_block;
public class Student {
static int number = 80;
static String schoolName = "黑马";
//静态代码块
static{
System.out.println("静态代码块执行了~~");
//schoolName = "黑马";
}
int age = 18;
//实例代码块
{
System.out.println("实例代码块执行了~~");
//age = 18;
System.out.println("有人创建了对象" + this);
}
public Student(){
System.out.println("无参数构造器执行了~~");
}
public Student(String name){
System.out.println("有参数构造器执行了~~");
}
}
package itchinajie.d5_block;
public class Test {
public static void main(String[] args) {
//目标:认识两种代码块,了解他们的特点和基本作用(认识就行)
System.out.println(Student.number);
System.out.println(Student.number);
System.out.println(Student.number);
System.out.println(Student.schoolName);//黑马
System.out.println("-----------------------------------");
Student s1 = new Student();
Student s2 = new Student("张三");
System.out.println(s1.age);
System.out.println(s2.age);
}
}
static的应用知识:单例设计模式
什么是设计模式?
1、一个问题通常有种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
2、设计模式有20多种,对应20多种软件开发中会遇到的问题。
饿汉式单例设计模式
把类的构造器私有;定义一个类变量存储类的一个对象;提供一个类方法返回对象。
package itchinajie.d6_singleIstance;
public class A {
//2、定义一个类变量记住类的一个对象
private static A a = new A();
//1、必须私有类的构造器
private A(){
System.out.println("牛逼");
}
//3、定义一个类方法返回类的对象
public static A getObject(){
return a;
}
}
饿汉式单例设计模式的一个特点就是在获取类的对象时,对象已经创建好了。
package itchinajie.d6_singleIstance;
public class Test {
public static void main(String[] args) {
//目标:掌握饿汉式单例设计模式的写法
A a1 = A.getObject();
A a2 = A.getObject();
System.out.println(a1);
System.out.println(a2);
}
}
饿汉式单例设计模式的一个特点就是在获取类的对象时,对象已经创建好了。
他适合做任务管理器对象、获取运行时对象。在这些业务场景下,使用单例模式,可以避免浪费内存。
而懒汉式单利设计模式是拿对象时,才开始创建对象。
懒汉式单利设计模式
package itchinajie.d6_singleIstance;
public class B {
//2、定义一个类变量,用于存储这个类的一个对象
private static B b;
//1、把类的构造器私有
private B(){
}
//3、定义一个类方法,这个方法要保证第一次调用时才创建一个对象,后面调用时都会用这同一个对象返回
public static B getInstance(){
if (b == null) {
System.out.println("第一次创建对象~");
b = new B();
}
return b;
}
}
package itchinajie.d6_singleIstance;
public class Test2 {
/*掌握懒汉式单例的写法*/
public static void main(String[] args) {
B b1 = B.getInstance();//第一次创建对象
B b2 = B.getInstance();
System.out.println(b1 == b2);
}
}
继承
继承:Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系。
继承的特点:子类能继承父类的非私有成员(成员变量、成员方法)
继承后对象的创建:子类的对象是由子类、父类共同完成的。
class 子类 extends 父类 {}
继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
子类又被称为派生类; 父类又被称为超类(Super Class)。
关于继承的详细内容请转一下文章:
java继承实现原理,快来get!_extends inheritance-CSDN博客
继承相关注意事项
权限修饰符
就是是用来限制类中的成员(成员变量、成员方法、构造器、代码块。)能够被访问的范围。
package itchinajie.d9_modifer;
public class Demo {
public static void main(String[] args) {
//目标:掌握不同权限修饰符的作用
Fu f = new Fu();
//f.privateMethod();
f.method();
f.protectedMethod();
f.publicMethod();
}
}
package itchinajie.d9_modifer;
public class Fu {
//1、私有:只能在本类中访问
private void privateMethod(){
System.out.println("==private==");
}
//2、缺省:本类,同一个包下的类
void method(){
System.out.println("==缺省==");
}
//3、protected:本类,同一个包下的类,任意包下的子类
protected void protectedMethod(){
System.out.println("==protected==");
}
//4、public:本类,同一个包下的类,任意包下的子类,在任意包下的任意类
public void publicMethod(){
System.out.println("==public==");
}
public void test(){
privateMethod();
method();
protectedMethod();
publicMethod();
}
}
单继承、Object类
Java是单继承的,Java中的类不支持多继承,但是支持多层继承。
一图便知为何不支持多继承:
object类是java所有类的祖宗类。我们写的任何一个类,其实都是object的子类或子孙类。
我们先创建3个类,最后让他们互相继承试验一下:
package itchinajie.d11_extends_feature;
public class Test {
public static void main(String[] args) {
//目标:掌握继承的两个注意事项
//1、Java是单继承的:一个类只能继承一个父类,Java中的类不支持多继承,但支持多层继承
//2、Object类是Java类中所有类的祖宗
}
class A{}//extends Object{}
class B extends A{}
//class C extends B , A{}//报错
class D extends B{}
}
方法重写
什么是方法重写?
当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、
参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
注意:重写后,方法的访问,Java会遵循就近原则。
直接上案例感受:
package itchinajie.d12_extends_override;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//目标:认识方法重写,掌握方法重写的常见应用场景
B b = new B();
b.print1();
b.print2(2,3);
System.out.println("--------------------");
Student s = new Student("播妞",19);
System.out.println(s.toString());//重写方法后 可使用
System.out.println(s);
ArrayList list = new ArrayList();
list.add("Java");
System.out.println(list);
}
}
package itchinajie.d12_extends_override;
public class A {
public void print1(){
System.out.println("111");
}
public void print2(int a,int b){
System.out.println("1111111");
}
}
package itchinajie.d12_extends_override;
public class B extends A{
//方法重写
@Override//安全,可读性好
public void print1(){
System.out.println("666");
}
//申明不变,重新实现
//方法重写
@Override
public void print2(int a,int b){
System.out.println("6666666");
}
}
package itchinajie.d12_extends_override;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
方法重写的其它注意事项:
重写小技巧:
1、使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好
2、子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public>protected>缺省)。
3、重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
私有方法、静态方法不能被重写,如果重写会报错的。
子类中访问其他成员的特点
1、在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。
先子类局部范围找。
然后子类成员范围找。
然后父类成员范围找,如果父类范围还没有找到则报错。
2、如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?
可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
package itchinajie.d13_extends_visit;
public class Test {
public static void main(String[] args) {
//目标:掌握子类中访问其他成员的特点,就近原则
Z z = new Z();
z.showName();
}
}
package itchinajie.d13_extends_visit;
public class F {
String name = "父类名字";
public void print(){
System.out.println("==父类的print方法执行==");
}
}
package itchinajie.d13_extends_visit;
public class Z extends F{
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println("name");//局部名称
System.out.println(this.name);//子类成员变量
System.out.println(super.name);//父类成员变量
}
}
子类构造器的特点
子类的全部构造器,都会先调用父类的构造器,再执行自己。
子类构造器为什么要调用父类的构造器,有啥应用场景?
子类构造器可以通过调用父类构造器,把对象中包含父类这部分的数据先初始化赋值,再回来把对象里包含子类这部分的数据也进行初始化赋值。
package itchinajie.d14_extends_constructor;
public class Test2 {
public static void main(String[] args) {
//目标:搞清楚子类构造器为什么要调用父类的构造器,有啥应用场景
Teacher t = new Teacher("张三",36,"Java");
System.out.println(t.getName());
System.out.println(t.getAge());
System.out.println(t.getSkill());
}
}
class Teacher extends People{
private String skill;
public Teacher(String name,int age,String skill){
super(name,age);
this.skill = skill;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
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 void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
补充:this(…)调用兄弟构造器
任意类的构造器中,是可以通过this(.....)去调用该类的其他构造器的。
this(.…)和super(.…)使用时的注意事项:
this(…)、super((.…)都只能放在构造器的第一行,因此,有了this(.…)就不能写super(.…)了,反之亦然。
(本章图片均来自于黑马程序员视频)