面向对象是Java编程的核心概念,如果不能充分了解面向对象的思想,那么会给你在实际的项目开发过程中,带来很多业务设计上的困扰。
我们在设计完一个类,在使用这个类去创建对象实例的时候,有些场景是需要对实例做初始化操作的,那么Java中提供了构造器来满足这方面要求。
默认无参数构造器:
class Rock{
printme(){
System.out.print("printme");
}
}
Rock r=new Rock();
有参数构造器:
class Rock {
Rock(String name) {
System.out.print("这是一个有参构造器:" + name);
}
}
Rock r = new Rock("IT飞牛");
//这是一个有参构造器:IT飞牛
构造器重载:
class Rock {
Rock(String name) {
System.out.println("这是一个有参构造器,姓名:" + name);
}
Rock(String name, int age) {
this(name);//这里使用this调用其他构造器,复用
System.out.println("这是一个有参构造器,年龄:" + age);
}
}
Rock r = new Rock("IT飞牛", 18);
//这是一个有参构造器,姓名:IT飞牛
//这是一个有参构造器,年龄:18
类中的一个方法,有些场景下需要根据不同的情况,做不同的操作,当然我们可以直接定义不同的方法就能实现。但是有些时候的确是同类型的操作,方法名最好是一样的,这时候我们可以使用重载来实现。
类中的重载主要是根据方法传入的参数类型列表,去自动识别调用不同的方法执行。
下面提供了同方法名,但是不同参数的案例:
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
Rock r = new Rock();
r.say();
r.say("你好");
r.say("你好", 3);
r.say(3, "你好");
}
}
class Rock {
void say() {
System.out.println("啥都没说");
}
void say(String msg) {
System.out.println("说:" + msg);
}
void say(String msg, int num) {
System.out.println(msg + ",被说了" + num + "遍");
}
void say(int num, String msg) {
System.out.println("说了" + num + "遍," + msg);
}
}
//啥都没说
//说:你好
//你好,被说了3遍
//说了3遍,你好
可以看到,不同的入参,调用了不同的同名方法。
涉及基本类型的重载,可能会由于变量类型提升,调用的方法有偏差。例如:Rock加入有个do
方法,调用r.do(1)
这个方法,参数会按照int
类型去识别。请看下面案例:“
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
Rock r = new Rock();
r.say(2);
r.say((byte) 2);
}
}
class Rock {
void say(byte num) {
System.out.println("这是byte");
}
void say(int num) {
System.out.println("这是int");
}
}
//这是int
//这是byte
this
就是一个变量,可以用在方法中,来拿到当前对象。
接触过js的小伙伴应该是比较好理解,java中的this的指向和js中的this的指向是一个原则:【谁调用,this就指向谁】
使用static final
修饰的成员变量就被称为常量。
static
方法是没有this
的,在static
方法内部不能调用this
。并且在static
内部不能调用非静态方法。static
方法。这实际上正是static
方法的主要用途,他很像全局方法。static
修饰,属于类,在计算机里只有一份,会被类的全部对象共享。package com.itfeiniu.hello;
public class Food {
String name; //这是每个Food实例的变量,每个实例都有独立的一个name属性。
static int num; //这是Food类的变量,所有类的实例共享一个变量。
}
使用static
定义的代码块称为静态代码块。一般用于初始化静态成员变量
public class Student {
public String id;
public String name;
public static String major;
static {
major = "软件工程";
System.out.println("执行了 静态代码块");
}
}
public class Demo {
private static int x = 1;
private int y = 2;
static {
System.out.println("静态代码块1,x=" + x);
x = 10;
}
{
System.out.println("实例代码块1,x=" + x + ", y=" + y);
y = 20;
}
public Demo() {
System.out.println("构造函数,x=" + x + ", y=" + y);
y = 30;
}
static {
System.out.println("静态代码块2,x=" + x);
x = 100;
}
{
System.out.println("实例代码块2,x=" + x + ", y=" + y);
y = 40;
}
public static void main(String[] args) {
Demo demo1 = new Demo();
Demo demo2 = new Demo();
}
}
合理隐藏 合理暴露
java类中的方法或者属性,可以设置访问范围,修饰符是:private
、protect
、public
。
private:该成员可以被该类内部成员访问;
default:该成员可以被该类内部成员访问,也可以被同一包下其他的类访问;
protected:该成员可以被该类内部成员访问,也可以被同一包下其他的类访问,还可以被它的子类访问;
public:该成员可以被任意包下,任意类的成员进行访问。
java
中的读写拦截器是基于public
方法来实现的。
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
Rock r = new Rock();
r.setName("张三");
System.out.println(r.getName());
r.setAge(20);
System.out.println(r.getAge());
}
}
class Rock {
private String name;
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;
}
private int age;
}
//张三
//20
可以使用IDEA快速生成getter
和setting
,如下图:
实体类的特点:
下面Student
类就是典型的实体类,整个类就是用来保存学生数据的,没有其他任何数据处理业务。一般实体类会配合实体类的操作类来使用,实体类存放数据,操作类StudentOperator
使用实体类的数据做数据处理业务。
代码如下:
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
Student std1 = new Student("张三", 60);
StudentOperator sto1 = new StudentOperator(std1);
sto1.printPass();
Student std2 = new Student("李四", 50);
StudentOperator sto2 = new StudentOperator(std2);
sto2.printPass();
}
}
class StudentOperator {
private Student student;
public StudentOperator(Student student) {
this.student = student;
}
public void printPass() {
if (student.getScore() >= 60) {
System.out.println(student.getName() + ",及格了");
} else {
System.out.println(student.getName() + ",没及格");
}
}
}
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
继承是实现软件复用的重要手段。
继承中最常使用的两个关键字是 extends 和 implements 。通过使用这两个关键字,我们能实现一个对象获取另一个对象的属性。
所有 Java 的类均是由 java.lang.Object 类继承而来的,所以 Object 是所有类的祖先类,而除了 Object 外,所有类必须有一个父类。
public interface Animal {}
public class Mammal implements Animal{
}
public class Dog extends Mammal{
}
Java不支持多继承,例如下面的写法是错误的:
public class extends Animal, Mammal{}
但是我们可以用接口来实现(多继承接口来实现),代码结构如下:
//Apple.java
package com.itfeiniu.hello;
class Fruit {
String color;
int kg;
String shape;
}
interface Fruit1 {
public void eat1();
public void travel1();
}
interface Fruit2 {
public void eat2();
public void travel2();
}
public class Apple extends Fruit implements Fruit1, Fruit2 {
public Apple(String color, int kg, String shape) {
this.color = color;
this.kg = kg;
this.shape = shape;
}
public void eat1() {
System.out.println("eat1");
}
public void travel1() {
System.out.println("travel1");
}
public void eat2() {
System.out.println("eat2");
}
public void travel2() {
System.out.println("travel2");
}
}
//helloworld.java
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
Apple app = new Apple("red", 5, "circle");
app.eat1();
app.eat2();
System.out.println(app.color);
}
}
//eat1
//eat2
//red
抽象类总结规定:
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
如果一个类包含抽象方法,那么该类必须是抽象类。
public abstract void Test{
public abstract void func();
}
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类必须声明为抽象类。
最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象从而失去意义。
package com.itfeiniu.hello;
public abstract class Food {
public abstract void print();
}
class Apple extends Food {
@Override
public void print() {
System.out.println("我是一个苹果");
}
}
class Test {
public static void main(String[] args) {
Apple a = new Apple();
a.print();
}
}
//我是一个苹果
继承抽象类的子类必须重写父类所有的抽象方法!
抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类(不包含抽象方法的抽象类,目的就是不想让调用者实例化该对象,通常用于某些特殊的类的结构设计)。
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何类型 final, static 字段
//抽象方法
}
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
实现一个接口的语法,可以使用这个公式:
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
package com.itfeiniu.hello;
interface Food {
String SCHOOL_NAME = "IT飞牛";
default void print1() {
this.print2();
}
private void print2() {
System.out.println("这里是私有方法print2");
}
public static void print3() {
System.out.println("这里是静态方法print3");
}
void print4();
}
class Apple implements Food {
@Override
public void print4() {
System.out.println("我是一个苹果," + this.SCHOOL_NAME);
}
}
class Test {
public static void main(String[] args) {
Apple a = new Apple();
a.print1();
// a.print2(); 报错
Food.print3();
a.print4();
}
}
//这里是私有方法print2
//这里是静态方法print3
//我是一个苹果,IT飞牛
package com.itfeiniu.hello;
interface A {
void test1();
}
interface B {
void test2();
}
//方式一
//interface C extends A, B {
// //这里实现接口多继承
//}
//class Food implements C {
//
// @Override
// public void test1() {
//
// }
//
// @Override
// public void test2() {
//
// }
//}
//方式二
class Food implements A,B{
@Override
public void test1() {
}
@Override
public void test2() {
}
}
需要注意的是,多继承时,需要确保父接口中不存在同名方法。如果存在同名方法,那么默认会报错,需要在自身类中重写这个同名的方法。
一个类同时继承一个父类并且实现一个或多个接口,如果父类和接口中存在同名方法,那么优先会继承父类中的同名方法,例如:
package com.itfeiniu.hello;
interface A {
public static void test1() {
System.out.println("这里是静态方法test1");
}
}
class D {
public void test1() {
System.out.println("这里是test1方法");
}
}
public class Food extends D implements A {
public void run() {
this.test1();
}
}
class HelloWorld2 {
public static void main(String[] args) {
Food f = new Food();
f.run();
}
}
//这里是test1方法
package com.itfeiniu.hello;
public class Food {
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.test();
//77
//88
//99
}
}
class Outer {
private int age = 99;
class Inner {
private int age = 88;
public void test() {
int age = 77;
System.out.println(age);
System.out.println(this.age);
System.out.println(Outer.this.age);
}
}
}
package com.itfeiniu.hello;
public class Food {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.test();
}
}
class Outer {
private int age;
public static String SchoolName="IT飞牛学院";
public static class Inner {
private String name;
public static int a;
public void test() {
System.out.println(SchoolName);
// System.out.println(age);//报错
}
}
public static void test2() {
System.out.println(SchoolName);
// System.out.println(age); //静态方法不能访问实例对象
}
}
//IT飞牛学院
可以直接访问外部类的静态成员,不能直接访问
就是以中国特殊的局部内部类;所谓匿名,指的就是不需要位这个类声明名字。
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
Animal animal = new Animal() {
@Override
public void cry() {
System.out.println("喵喵");
}
@Override
public void eat() {
System.out.println("我吃猫粮");
}
};
animal.cry();
animal.eat();
}
}
abstract class Animal {
public abstract void cry();
public abstract void eat();
}
//喵喵
//我吃猫粮
匿名内部类作为方法入参:
package com.itfeiniu.hello;
public class HelloWorld {
public static void main(String[] args) {
go(new Animal() {
@Override
public void cry() {
System.out.println("喵喵");
}
});
// 相同效果
// Animal animal = new Animal() {
// @Override
// public void cry() {
// System.out.println("喵喵");
// }
// };
// go(animal);
}
public static void go(Animal a) {
a.cry();
}
}
interface Animal {
public abstract void cry();
}
//喵喵
一个窗口应用程序中,匿名内部类的应用:
package com.itfeiniu.hello;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class HelloWorld {
public static void main(String[] args) {
//1、创建窗口
JFrame win = new JFrame("登录界面");
JPanel panel = new JPanel();
win.add(panel);
JButton btn = new JButton("登录");
panel.add(btn);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win, "登录一下");
}
});
//用兰姆达表达式替代,效果同上
// btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "登录一下"));
win.setSize(400, 400);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
win.setVisible(true);
}
}
可以通过 super
关键字来实现对父类成员的访问,用来引用当前对象的父类。
final修饰类,类不能被继承
final class A{
public final void
}
//class B extend A{}
final修饰方法,方法不能被重写
class C{
public final void test(){}
}
class D extends C{
//@override
//public void test(){}
}
final修饰变量,总规则:有且仅能赋值一次
class Test{
public static void main(String[] args){
final int a;
a=12;
//a=13; 再次赋值时报错
final int[] arr={11,22,33};
arr[1]=222;//这是可以的
//arr={22,33};//报错
}
}
使用static final
修饰的成员变量就被称为常量
//通常用于记录系统的配置信息
public class Constant{
public static final String SCHOOL_NAME="IT飞牛";
}