1)类变量:有 static 修饰,属于类,在计算机里只有一份, 会被类的全部对象共享。
2)实例变量(对象的变量):无 static 修饰,属于对象。
public class CLASS {
static String name;
int age;
}
public class Test {
public static void main(String[] args) {
// 类名.类变量
CLASS.name = "wyb";
// 对象名.实例变量
CLASS c = new CLASS();
c.age = 13;
}
}
1)类方法:有 static 修饰,属于类。
2)实例方法(对象的方法):无 static 修饰,属于对象。
补充:main 方法
main 方法也属于一种类方法,它也可以传入参数。
在程序执行时,JVM虚拟机会直接调用 main 方法。
1)类方法中可以直接访问类的成员,不可以直接访问实例成员。
2)实例方法中既可以直接访问类成员,也可以直接访问实例成员。
3)实例方法中可以出现 this 关键字,类方法中不可以出现 this 关键字的。
( this 是用来指向调用方法的对象,而类方法只针对类)
代码块是类的5大成分之一(成员变量,成员方法,构造器,代码块,内部类)
1)静态代码块
格式:static { }
特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。
作用:完成类的初始化,例如对类变量的初始化赋值。
public class CLASS {
static String name;
static int age;
static {
name = "wyb";
age = 20;
}
}
public class Test {
public static void main(String[] args) {
System.out.println(CLASS.name);
System.out.println(CLASS.age);
}
}
2)实例代码块
格式:{ }
特点:每次创建对象时,执行实例代码块,并在构造器前执行。
作用:和构造器一样,都是用来完成对象的初始化的,例如对实例变量进行初始化赋值。
作用:确保一个类只有一个对象
(1)饿汉式单例(创建类时创建对象)
方法:1)把类的构造器私有(为了使外部无法创建对象)
2)定义一个类变量,记住类的对象
3)定义一个类方法,返回对象
public class CLASS {
private static CLASS student = new CLASS();
private CLASS() {}
public static CLASS getStudent()
{
return student;
}
}
(2)懒汉式单例(需要对象时创建对象)
方法:1)把类的构造器私有(为了使外部无法创建对象)
2)定义一个类变量用于存储类的对象
3)定义一个类方法,第一次调用时创建对象,再返回对象
public class CLASS {
private static CLASS student;
private CLASS() {}
public static CLASS getStudent()
{
if(student == null)
{
student = new CLASS();
}
return student;
}
}
定义:利用 extends 关键字,使一个类和另一个类建立父子关系
特点:子类能够继承父类的非私有成员变量和成员方法
对象的创建:由子类和父类共同完成
public class A {
public int a;
public void print1()
{
System.out.println("--public print--");
}
private int b;
private void print2()
{
System.out.println("--private print--");
}
}
public class B extends A{
public int b;
public void print3()
{
System.out.println(a);
print1();
//System.out.println(b);
//print2();
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
System.out.println(b.a);
//System.out.println(b.b);
b.print1();
//b.print2();
b.print3();
}
}
作用:限制类中的成员能够被访问的范围
分类:public private protected 【缺省】
Java只有单继承,不支持多继承,但可以多层继承
例:A => B => C C可以继承A和B的非私有成员
public class A {
public int i;
public void print1()
{
System.out.println("--public print--");
}
private int j;
private void print2()
{
System.out.println("--private print--");
}
}
public class B extends A{
public int k;
public void print3()
{
System.out.println(a);
print1();
//System.out.println(b);
//print2();
}
}
public class C extends B{
public void print4()
{
System.out.println(i);
//System.out.println(j);
System.out.println(k);
}
}
public class Test {
public static void main(String[] args) {
C c = new C();
c.print1();
//c.print2();
c.print3();
c.print4();
}
}
Object 类是Java中所有类的祖类,Object 类不需要在创建类时声明(默认继承)
Object 类的常用方法有:hascode,toString
1)toString
能够返回对象的地址,在打印对象本体时默认调用,一般用于重写 toString 方法后返回对象内容
作用:当父类方法需求不能满足子类时,子类可以重写一个与父类方法名称和参数列表相同的方法
注意事项:1)重写之后,方法的访问遵循就近原则
2)重写小技巧: 使用 @Override 注解,他可以指定 java 编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
3)子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限。
4)重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
5)私有方法、静态方法不能被重写,如果重写会报错的。
public class A {
public int i;
public void print()
{
System.out.println("666");
}
public void func(int a,int b)
{
System.out.println(a+b);
}
}
public class B extends A{
@Override
public void print()
{
System.out.println("abc");
}
@Override
public void func(int a,int b)
{
System.out.println(a-b);
}
}
应用:重写 Object 类的 toString 方法
1)就近原则
2)访问本类成员用this,访问父类成员用super
特点:调用子类构造器前,会先调用父类的无参构造器,在执行自己,即在创建子类构造器时默认包含了 super()
class X{
public X() {
System.out.println("1");
}
}
class Y extends X{
public Y() {
//super(); 子类构造器默认存在
System.out.println("2");
}
public Y(int a) {
//super(); 子类构造器默认存在
System.out.println("3");
}
}
注意:1)如果父类没有无参构造器,程序会出错,可以手动书写 super() 函数;
2)如果想要调用父类的有参构造器,一定要创建同参子类构造器并声明super()
3)super() 只能在构造器第一行声明
class Student{
public Student(){
}
public Student(String name,int age)
{
this(name,age,60);
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String name;
public int age;
public int score;
}
注意:同 super() 一样,this() 也只能在构造器第一行声明,并且这两者不能同时在一个构造器里声明
Java中分为对象多态和行为多态,但是属性(成员变量)无多态
必须有相对的继承关系才能实现多态操作,并且子类要求对父类的方法重写
class People{
public String name = "People";
public void showClass(){
System.out.println("People方法");
}
}
class Teacher extends People{
public String name = "Teacher";
public void showClass(){
System.out.println("Teacher方法");
}
}
class Student extends People{
public String name = "Student";
public void showClass(){
System.out.println("Student方法");
}
}
public class Test {
public static void main(String[] args) {
People p1 = new Teacher(); // 对象多态
System.out.println(p1.name);// 成员变量无多态
p1.showClass(); // 行为多态
People p2 = new Student();
System.out.println(p2.name);// 编译看左边,运行也看左边
p2.showClass(); // 编译看左边,运行看右边
}
}
注意:编译时只能调用父类的成员方法,但运行时执行的是子类重写后的成员方法
1)在多态的形式下,new的对象是解耦合的
//People p = new Teachar();
People p = new Student();
new 创建的对象类型随时可以改变
2)方法在调用对象时,只需要通过父类的对象类型接收,即可接收所有多态形式下的子类对象
public class Test {
public static void main(String[] args) {
People p1 = new Teacher();
People p2 = new Student();
printName(p1);
printName(p2);
}
public static void printName(People p){
System.out.println(p.getName());
}
}
在多态形式下,无法访问子类独有的成员方法
解决方案:强制类型转换(只能转换相对应的子类类型)
public class Test {
public static void main(String[] args) {
People p1 = new Teacher();
Teacher t1 = (Teacher) p1;
People p2 = new Student();
Teacher t2 = (Teacher) p2;
}
}
代码如上,p1 完成了类型转换,但 p2 报错
补充:instanceof 关键字,用于判断对象类型是否对应
People p1 = new Teacher();
if(p1 instanceof Teacher) {
Teacher t1 = (Teacher) p1;
}
else {
Student s1 = (Student) p1;
}
修饰类:该类被称为最终类,特点是不能被继承了。
修饰方法:该方法被称为最终方法,特点是不能被重写了。
修饰变量:该变量有且只能赋值一次。
public class Test {
// final修饰静态成员变量 -> 常量
public static final String S_NAME = "cxk";
// final修饰实例成员变量
private final int age = 11;
public static void main(String[] args) {
// final修饰基本类型变量
final String name = "fzc";
// final修饰引用类型变量
final int[] arr = {1,2,3};
}
}
注意:1)final修饰的静态成员变量即为常量
2)final修饰基本类型的变量,该变量的值不可改变
3)final修饰引用类型的变量,该变量的地址不可改变
使用 static final 修饰的成员变量即为常量
作用:通常用于记录系统的配置信息
命名规范:单词全部大写,单词与单词之间用_连接
优点:1)代码可读性更好,可维护性也更好。
2)程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
1)abstract 关键字:用于修饰类和成员方法
abstract class People{
public abstract void fun();
}
注意:修饰的成员方法不能有方法体
1)抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
2)类该有的成员(成员变量、方法、构造器)抽象类都可以有。
3)抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
4)一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
最大的优势在于能够更好地支持多态
class Teacher extends People{
public Teacher(String name) {
super(name);
}
@Override
public void action() {
System.out.println("老师在教书");
}
}
class Student extends People{
public Student(String name) {
super(name);
}
@Override
public void action() {
System.out.println("学生在学习");
}
}
abstract class People{
private String name;
public People(String name) {
this.name = name;
}
public void printName() {
System.out.print(name);
}
public abstract void action();
}
public class Test {
public static void main(String[] args) {
People p1 = new Student("fzc");
p1.printName();
p1.action();
People p2 = new Teacher("cxk");
p2.printName();
p2.action();
}
}
上述代码为 People 父类下,Student 和 Teacher 两个子类对象,通过抽象方法重写,来创建对象
用于解决大量的重复代码
1)定义一个抽象类。
2)在里面定义2种方法:模板方法 -- 把相同代码放里面去。
抽象方法 -- 具体实现交给子类完成。
上一小节的代码就已经体现出来了这种设计模式,这里不再举例
注意:如果模版方法不再修改,建议用 final 修饰
1)interface 关键字:用于定义接口
interface People{
// 默认为常量,不需要static final修饰
String NAME = "cxk";
// 默认为抽象方法,不需要abstract修饰
void action();
}
接口中只能定义成员变量(常量)和成员方法(抽象方法),且不能直接创建对象
功能:用来被类实现(implements),实现接口的类叫做实现类
public interface A {
void testA1();
void testA2();
}
public interface B {
void testB1();
void testB2();
}
// 实现类
public class C implements A,B{
@Override
public void testA1() {}
@Override
public void testA2() {}
@Override
public void testB1() {}
@Override
public void testB2() {}
}
注意:一个类可以实现多个接口,但必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。
1)弥补了类单继承的不足,一个类在继承其他类的同时,也可以实现多个接口。
2)让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。
1)创建一个 Student 类,用于存储单个学生的信息
public class Student {
private String name;
private String sex;
private double score;
public Student() {
}
public Student(String name, String sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
包含三种属性,对应的 get,set 方法和用于初始化的构造器
2)创建一个 ClassManager 类,用于存储全部学生信息和实现题目需求的操作
public class ClassManager {
private ArrayList students = new ArrayList<>();
public ClassManager() {
students.add(new Student("A","男",100));
students.add(new Student("B","女",60));
students.add(new Student("C","女",77));
students.add(new Student("D","男",88));
}
public void printInfo(){
}
public void printScore(){
}
}
使用 ArrayList 集合存储学生信息,利用构造器初始化,初步定义两个实现功能的方法
3)因为两种功能各有两种方案,所以利用面向接口编程思想,通过接口创建实现类
public interface StudentOperator {
void printInfo(ArrayList students);
void printScore(ArrayList students);
}
4)创建两个实现类
public class StudentOperatormpl1 implements StudentOperator{
@Override
public void printInfo(ArrayList students) {
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println("姓名:"+s.getName()+" 性别:"+s.getSex()+" 分数:"+s.getScore());
}
}
@Override
public void printScore(ArrayList students) {
double allsroce = 0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
allsroce += s.getScore();
}
System.out.println("平均分为:"+allsroce/students.size());
}
}
public class StudentOperatormpl2 implements StudentOperator{
@Override
public void printInfo(ArrayList students) {
int manCount = 0,womanCount = 0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println("姓名:"+s.getName()+" 性别:"+s.getSex()+" 分数:"+s.getScore());
if(s.getSex().equals("男")){
manCount += 1;
}
else{
womanCount += 1;
}
}
System.out.println("男生人数为:"+manCount+"女生人数为"+womanCount);
}
@Override
public void printScore(ArrayList students) {
double allsroce = 0,max = 0,min = 100;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
allsroce += s.getScore();
max = max>s.getScore()?max:s.getScore();
min = min
5)在 ClassManager 类下调用实现类的功能
public class ClassManager {
private ArrayList students = new ArrayList<>();
//private StudentOperator studentOperator = new StudentOperatormpl1();
private StudentOperator studentOperator = new StudentOperatormpl2();
public ClassManager() {
students.add(new Student("A","男",100));
students.add(new Student("B","女",60));
students.add(new Student("C","女",77));
students.add(new Student("D","男",88));
}
public void printInfo(){
studentOperator.printInfo(students);
}
public void printScore(){
studentOperator.printScore(students);
}
}
6)进行测试
public class Test {
public static void main(String[] args) {
ClassManager cs = new ClassManager();
cs.printInfo();
cs.printScore();
}
}
补充:步骤五的实现利用了接口的多态创建实现类的对象
private StudentOperator studentOperator = new StudentOperatormpl2();
注意:接口不能直接利用接口名创建对象,但可以通过多态的形式,利用实现类来创建对象
1)默认方法(实例方法):使用 default 修饰,默认被 public 修饰,只能使用接口的实现类来调用
2)私有方法:必须使用 private 修饰,用于被接口中其他方法调用
3)接口方法(静态方法):使用 static 修饰,默认被 public 修饰,可以直接通过接口调用
public interface A {
default void test1() {
test2();
}
private void test2(){
}
static void test3(){
}
}
Java中接口可以继承,并且可以多继承
interface A{
void test1();
}
interface B{
void test2();
}
interface C extends B,A{
}
class D implements C{
@Override
public void test1() {
}
@Override
public void test2() {
}
}
注意事项:
1)一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
2)一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
3)一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
4)一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块)
定义:如果一个类定义在另一个类的内部,这个类就是内部类。
应用场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。
将内部类视为外部类的一个成员,内部类可以随意调用外部类的成员,外部类也可以创建内部类对象来访问内部类
public class Test {
public static void main(String[] args) {
Outer o1 = new Outer();
o1.test1();
Outer.Inner o2 = new Outer().new Inner();
o2.test2();
}
}
class Outer{
private int a = 100;
public void test1() {
Inner i1 = new Inner();
i1.test2();
}
public class Inner{
private int a = 90;
public void test2(){
int a = 80;
System.out.println(a);
System.out.println(this.a);
System.out.println(Outer.this.a);
}
}
}
注意:1)在外部类的外部创建内部类对象,要下面这种形式:
Outer.Inner o2 = new Outer().new Inner();
2)如果外部类、内部类、内部类方法定义的变量重名,要逐级访问,如下:
System.out.println(a);
System.out.println(this.a);
System.out.println(Outer.this.a);
public class Test {
public static void main(String[] args) {
Outer.Inner o2 = new Outer.Inner();
o2.test2();
}
}
class Outer{
private int a = 100;
public static int b = 10;
public static class Inner{
public void test2(){
//System.out.println(a);
System.out.println(b);
}
}
}
注意:1)静态内部类可以直接访问外部类的静态变量,但不可以直接访问实例变量
2)静态内部类创建对象不需要以外部类的对象为基础来创建了
Outer.Inner o2 = new Outer.Inner();
定义在方法,代码块,构造器中的内部类(目前不需要学)
1)匿名内部类:用于直接创建一个对象的临时类,一般配合抽象类和接口使用
public class Test {
public static void main(String[] args) {
People t = new People() {
@Override
public void action() {
System.out.println("老师在教书");
}
};
t.action();
}
}
abstract class People{
public String name;
public abstract void action();
}
注意:创建匿名内部类时,Java首先会编译出来一个子类,根据这个子类来直接创建一个对象
2)匿名内部类的应用场景
通常作为一个参数传输给方法
枚举是一个特殊的类
public enum A {
X,Y,Z;
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
// A a = new A();
A a1 = A.X;
System.out.println(a1);// 可以直接打印枚举常量名
A a2 = A.Y;
a2.setName("cxk");
System.out.println(a2.getName());
A[] as = A.values(); // 将枚举类的所有常量存储到一个数组中
A a3 = A.valueOf("Z"); // 利用对应的常量名获取常量
System.out.println(a3.name()); // 获取对应的常量名
System.out.println(a3.ordinal()); // 获取对应的索引
}
}
根据上述代码,枚举类有以下特点:
1)枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
2)枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
3)枚举都是最终类,不可以被继承。
4)枚举类中,从第二行开始,可以定义类的其他各种成员。
5)编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法 。
在枚举中定义抽象方法
public enum A {
X("cxk"){
@Override
public void showName() {
System.out.println(getName());
}
},
Y("fzc"){
@Override
public void showName() {
System.out.println(getName());
}
};
A() {
}
A(String name){
this.name = name;
}
public abstract void showName();
public String name;
public String getName() {
return name;
}
}
注意:定义抽象枚举时,必须对常量对象进行方法重写
常用于表示一组信息,对功能进行标记和分类
例:以男女分类,创建不同的功能
enum Constant{
BOY,GIRL;
}
public class Test {
public static void main(String[] args) {
check(Constant.BOY);
check(Constant.GIRL);
}
public static void check(Constant sex) {
switch (sex)
{
case BOY:
System.out.println("看美女~~");
break;
case GIRL:
System.out.println("看帅哥~~");
break;
}
}
}
在定义类、接口、方法时,同时声明了一个或者多个类型变量(如:
作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力。这样可以避免强制类型转换,及其可能出现的异常。
泛型的本质:把具体的数据类型作为参数传给类型变量。
public class Test {
public static void main(String[] args) {
myArrayList arrayList1 = new myArrayList();
myArrayList arrayList2 = new myArrayList<>();
}
}
class myArrayList{
private Object[] arr = new Object[10];
private int size;
public boolean add(E e){
arr[size++] = e;
return true;
}
public E get(int index) {
return (E)arr[index];
}
}
注意:1)泛型类可以声明多个泛型
class myClass1{
}
2)泛型类声明泛型时可以添加限制条件
class myClass2{
}
和泛型类使用大同小异
class People{
}
class Teacher extends People{
}
interface PeopleData{
void add(E e);
String getName(E e);
}
class TeacherData implements PeopleData{
@Override
public void add(Teacher teacher) {
}
@Override
public String getName(Teacher teacher) {
return null;
}
}
注意:泛型接口也可以声明多个泛型,也可以加限制条件
class Car{
}
class BWM extends Car{
}
class BENZ extends Car{
}
public static void test(ArrayList e){
}
泛型方法也可以声明多个泛型,也可以加限制条件
补充:
1)?通配符:用于代表泛型,可以替代一切类型
上面的代码也可以改成以下形式:
public static void test(ArrayList extends Car> e){
}
2)泛型上下限
? extends Car 即为上限,接收的是 Car 类及其子类
? super Car 即为下限,接收的是 Car 类及其父类
1)泛型是工作在编译阶段的,一旦程序编译成class文件, class文件中就不存在泛型了,这就是泛型擦除。
2)泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。