类与对象的定义:
类:一种类 事物类,比如说:人类
对象:一种类的具体的对象,比如说具体到哪个人:lbw
在源文件src中创建Main类,右键src,命名记得首字母要大写
类中可以 定义一些变量
public class Main {
String name="li";
int age=18;
String sex="nan";
}
有时会报错:
记得构造项目(就是按一下锤子)
解决方法:如图
没有问题后:
new关键词 就是新建一个Main的对象 命名为main
语法就是这一句:
Main main=new Main();
利用.语法进行对对象内的值进行调用
public class Main1 {
public static void main(String[] args) {
Main main=new Main();
System.out.println(main.name);
}
}
方法:
和C语言中的函数一样
返回值 方法名称(){
方法体
}
返回值可以是void int Srting double等等
方法名称随意与变量一致 不过小写字母好点
void:没有返回值 就是不用return的意思
如果有返回值的话 就要return对应的返回值否则就会报错
public class Main {
String name="li";
int age=18;
String sex="nan";
void hello(){
System.out.println("我叫 "+name+" 今年 "+age+" 岁了!");
}
int sum(int a, int b){ //这里的参数,相当于我们在函数中定义了两个局部变量,我们可以直接在函数中使用
int c = a + b; //直接c = a + b
return c;
}
void setName(String name) {
this.name = name; //让当前对象的name变量值等于参数传入的值
}
}
public class Main1 {
public static void main(String[] args) {
Main main=new Main();
System.out.println(main.sum(1, 2));
main.hello();
}
}
this:
当出现重名时 this用来针对对象本身=右边的name就是String name方法引入的name
=的左边就是对象中的name 即String name="li";
方法的重载:
有些时候,参数类型可能会多种多样,我们的方法需要能够同时应对多种情况:
这种多情况会自适应选择调用
int sum(int a, int b){
return a + b;
}
double sum(double a, double b){ //为了支持小数加法,我们可以进行一次重载
return a + b;
}
方法对自己的调用(递归)
int test(int n){
if(n == 0) return 0;
return test(n - 1) + n; //返回的结果是下一层返回的结果+当前这一层的n
}
构造方法:
构造方法不需要填写返回值,并且方法名称与类名相同
就是有个默认的构造方法new 当然也可以自己写一个构造方法
(没必要不如用new)直接Person p = new Person("小明", 18, "男");就行
public class Person {
String name;
int age;
String sex;
public Person() { //反编译中,多出来了这样一个方法,这其实就是构造方法
}
}
public static void main(String[] args) {
Person p = new Person(); //这里的new Person()其实就是在调用无参构造方法
System.out.println(p.name);
}
public class Person {
String name;
int age;
String sex;
Person(String name, int age, String sex){ //跟普通方法是一样的
this.name = name;
this.age = age;
this.sex = sex;
}
}
public static void main(String[] args) {
Person p = new Person("小明", 18, "男"); //调用自己定义的带三个参数的构造方法
System.out.println(p.name);
}
静态变量和静态方法:
(全局都不改变的值) 因为静态方法属于类的,所以说我们在静态方法中,无法获取成员变量的值
同样的,在静态方法中,无法使用this关键字,因为this关键字代表的是当前的对象本身
但是静态方法是可以访问到静态变量的
静态变量的构造在代码之前
static String info; //这里我们定义一个info静态变量
static void test(){
System.out.println("我是静态方法");
}
public class Person {
String name = test(); //这里我们用test方法的返回值作为变量的初始值,便于观察
int age;
String sex;
{
System.out.println("我是普通代码块");
}
Person(){
System.out.println("我是构造方法");
}
String test(){
System.out.println("我是成员变量初始化");
return "小明";
}
static String info = init(); //这里我们用init静态方法的返回值作为变量的初始值,便于观察
static {
System.out.println("我是静态代码块");
}
static String init(){
System.out.println("我是静态变量初始化");
return "test";
}
}
包和访问控制:
包:
import导包
package 用自己的包
最常见的import java.io.*;
package com.test;
import com.test.entity.Person; //使用import关键字导入其他包中的类
import java.io.*;
public class Main {
public static void main(String[] args) {
Person person = new Person(); //只有导入之后才可以使用,否则编译器不知道这个类从哪来的
}
}
访问权限控制:
当前类 | 同一个包下的类 | 不同包下的子类 | 不同包下的类 | |
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
默认 | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
封装、继承、多态、抽象类、接口:
封装:
封装的目的是为了保证变量的安全性,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员,如果不进行封装,类中的实例变量可以直接查看和修改,可能给整个代码带来不好的影响,因此在编写类时一般将成员变量私有化,外部类需要使用Getter和Setter方法来查看和设置变量。
public class Person {
private String name; //现在类的属性只能被自己直接访问
private int age;
private String sex;
public Person(String name, int age, String sex) { //构造方法也要声明为公共,否则对象都构造不了
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name; //想要知道这个对象的名字,必须通过getName()方法来获取,并且得到的只是名字值,外部无法修改
}
public String getSex() {
return sex;
}
public int getAge() {
return age;
}
}
public static void main(String[] args) {
Person person = new Person("小明", 18, "男");
System.out.println(person.getName()); //只能通过调用getName()方法来获取名字
}
继承:
extends:
使用extends后 子类可以有父类的变量和方法
final 不可以被继承
当一个类继承另一个类时,属性会被继承,可以直接访问父类中定义的属性,除非父类中将属性的访问权限修改为
private,那么子类将无法访问(但是依然是继承了这个属性的):
public class Worker extends Person{ //工人类
}
public final class Person { //class前面添加final关键字表示这个类已经是最终形态,不能继承
}
public class Person {
String name;
int age;
String sex;
public void hello(){
System.out.println("我叫 "+name+",今年 "+age+" 岁了!");
}
}
public class Student extends Person{
public void study(){
System.out.println("我的名字是 "+name+",我在学习!"); //可以直接访问父类中定义的name属性
}
}
public static void main(String[] args) {
Student student = new Student();
student.study(); //子类不仅有自己的独特技能
student.hello(); //还继承了父类的全部技能
}
public class Student extends Person{
public Student(String name, int age, String sex) { //因为学生职业已经确定,所以说学生直接填写就可以了
super(name, age, sex, "学生"); //使用super代表父类,父类的构造方法就是super()
}
public void study(){
System.out.println("我的名字是 "+name+",我在学习!");
}
}
public class Worker extends Person{
public Worker(String name, int age, String sex) {
super(name, age, sex, "工人"); //父类构造调用必须在最前面
System.out.println("工人构造成功!"); //注意,在调用父类构造方法之前,不允许执行任何代码,只能在之后执行
}
}
public static void main(String[] args) {
Person person = new Student("小明", 18, "男"); //这里使用父类类型的变量,去引用一个子类对象(向上转型)
person.hello(); //父类对象的引用相当于当做父类来使用,只能访问父类对象的内容
}
public static void main(String[] args) {
Person person = new Student("小明", 18, "男");
Student student = (Student) person; //使用强制类型转换(向下转型)
student.study();
}
public static void main(String[] args) {
Person person = new Worker("小明", 18, "男"); //实际创建的是Work类型的对象
Student student = (Student) person;
student.study();
}
public void work(){
System.out.println("我是 "+super.name+",我在工作!"); //这里使用super.name来表示需要的是父类的name变量
}
public static void main(String[] args) {
Person person = new Student("小明", 18, "男");
if(person instanceof Student) { //我们可以使用instanceof关键字来对类型进行判断
System.out.println("对象是 Student 类型的");
}
if(person instanceof Person) {
System.out.println("对象是 Person 类型的");
}
}
虽然我们这里使用的是父类类型引用的对象,但是这并不代表子类就彻底变成父类了,这里仅仅只是当做父类使用而已。
我们也可以使用强制类型转换,将一个被当做父类使用的子类对象,转换回子类:
但是注意,这种方式只适用于这个对象本身就是对应的子类才可以,如果本身都不是这个子类,或者说就是父类,那么会出现问题:
那么,在子类存在同名变量的情况下,怎么去访问父类的呢?我们同样可以使用super关键字来表示父类
如果变量所引用的对象是对应类型或是对应类型的子类,那么instanceof都会返回true,否则返回false。
实际上所有类都默认继承自Object类,除非手动指定继承的类型,但是依然改变不了最顶层的父类是Object类。
方法的重写:
这其实就是面向对象编程中多态特性的一种体现。
注意,方法的重写不同于之前的方法重载,不要搞混了,方法的重载是为某个方法提供更多种类,而方法的重写是覆盖原有的方法实现,比如我们现在不希望使用Object类中提供的equals方法,那么我们就可以将其重写了
public class Person{
@Override //重写方法可以添加 @Override 注解,有关注解我们会在最后一章进行介绍,这个注解默认情况下可以省略
public boolean equals(Object obj) { //重写方法要求与父类的定义完全一致
if(obj == null) return false; //如果传入的对象为null,那肯定不相等
if(obj instanceof Person) { //只有是当前类型的对象,才能进行比较,要是都不是这个类型还比什么
Person person = (Person) obj; //先转换为当前类型,接着我们对三个属性挨个进行比较
return this.name.equals(person.name) && //字符串内容的比较,不能使用==,必须使用equals方法
this.age == person.age && //基本类型的比较跟之前一样,直接==
this.sex.equals(person.sex);
}
return false;
}
}
public static void main(String[] args) {
Person p1 = new Student("小明", 18, "男");
Person p2 = new Student("小明", 18, "男");
System.out.println(p1.equals(p2)); //此时由于三个属性完全一致,所以说判断结果为真,即使是两个不同的对象
}
有时候为了方便查看对象的各个属性,我们可以将Object类提供的toString方法重写了:
注意,静态方法不支持重写,因为它是属于类本身的,但是它可以被继承。
@Override
public String toString() { //使用IDEA可以快速生成
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", profession='" + profession + '\'' +
'}';
}
public static void main(String[] args) {
Person person = new Student("小明", 18, "男");
System.out.println(person);
}
基于这种方法可以重写的特性,对于一个类定义的行为,不同的子类可以出现不同的行为,比如考试,学生考试可以得到A,而工人去考试只能得到D:
public class Person {
...
public void exam(){
System.out.println("我是考试方法");
}
}
public class Student extends Person{
...
@Override
public void exam() {
System.out.println("我是学生,我就是小镇做题家,拿个 A 轻轻松松");
}
}
public class Worker extends Person{
...
@Override
public void exam() {
System.out.println("我是工人,做题我并不擅长,只能得到 D");
}
}
public static void main(String[] args) {
Person person = new Student("小明", 18, "男");
person.exam();
person = new Worker("小强", 18, "男");
person.exam();
}
注意,我们如果不希望子类重写某个方法,我们可以在方法前添加final关键字,表示这个方法已经是最终形态:
或者,如果父类中方法的可见性为private,那么子类同样无法访问,也就不能重写,但是可以定义同名方法:
抽象类:
public abstract class Person { //通过添加abstract关键字,表示这个类是一个抽象类
String name; //大体内容其实普通类差不多
int age;
String sex;
String profession;
Person(String name, int age, String sex, String profession) {
this.name = name;
this.age = age;
this.sex = sex;
this.profession = profession;
}
public abstract void exam(); //抽象类中可以具有抽象方法,也就是说这个方法只有定义,没有方法体
}
public class Worker extends Person{
public Worker(String name, int age, String sex) {
super(name, age, sex, "工人");
}
@Override
public void exam() { //子类必须要实现抽象类所有的抽象方法,这是强制要求的,否则会无法通过编译
System.out.println("我是工人,做题我并不擅长,只能得到 D");
}
}
public abstract class Student extends Person{ //如果抽象类的子类也是抽象类,那么可以不用实现父类中的抽象方法
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}
@Override //抽象类中并不是只能有抽象方法,抽象类中也可以有正常方法的实现
public void exam() {
System.out.println("我是学生,我就是小镇做题家,拿个 A 轻轻松松");
}
}
要实现这样的操作,我们可以将人类变成抽象类,抽象类比类还要抽象:
而具体的实现,需要由子类来完成,而且如果是子类,必须要实现抽象类中所有抽象方法:
抽象类由于不是具体的类定义(它是类的抽象)可能会存在某些方法没有实现,因此无法直接通过new关键字来直接创建对象:
要使用抽象类,我们只能去创建它的子类对象。抽象类一般只用作继承使用,当然,抽象类的子类也可以是一个抽象类:
注意,抽象方法的访问权限不能为private:因为抽象方法一定要由子类实现,如果子类都访问不了,那么还有什么意义呢?所以说不能为私有。
接口:
接口甚至比抽象类还抽象,他只代表某个确切的功能!也就是只包含方法的定义,甚至都不是一个类!接口一般只代表某些功能的抽象,接口包含了一些列方法的定义,类可以实现这个接口,表示类支持接口代表的功能(类似于一个插件,只能作为一个附属功能加在主体上,同时具体实现还需要由主体来实现)
public interface Study { //使用interface表示这是一个接口
void study(); //接口中只能定义访问权限为public抽象方法,其中public和abstract关键字可以省略
}
public class Student extends Person implements Study { //使用implements关键字来实现接口
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}
@Override
public void study() { //实现接口时,同样需要将接口中所有的抽象方法全部实现
System.out.println("我会学习!");
}
}
public class Teacher extends Person implements Study {
protected Teacher(String name, int age, String sex) {
super(name, age, sex, "教师");
}
@Override
public void study() {
System.out.println("我会加倍学习!");
}
}
public class Student extends Person implements Study, A, B, C { //多个接口的实现使用逗号隔开
}
public interface Study {
public static final int a = 10; //接口中定义的静态变量只能是public static final的
public static void test(){ //接口中定义的静态方法也只能是public的
System.out.println("我是静态方法");
}
void study();
}
public static void main(String[] args) {
System.out.println(Study.a);
Study.test();
}
接口不同于继承,接口可以同时实现多个:
我们可以让类实现这个接口:用implements关键词
所以说有些人说接口其实就是Java中的多继承,但是我个人认为这种说法是错的,实际上实现接口更像是一个类的功能列表,作为附加功能存在,一个类可以附加很多个功能,接口的使用和继承的概念有一定的出入,顶多说是多继承的一种替代方案。
接口跟抽象类一样,不能直接创建对象,但是我们也可以将接口实现类的对象以接口的形式去使用:
接口不同于类,接口中不允许存在成员变量和成员方法,但是可以存在静态变量和静态方法,在接口中定义的变量只能是:
枚举类:
public class Student extends Person implements Study {
private String status; //状态,可以是跑步、学习、睡觉这三个之中的其中一种
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
public enum Status { //enum表示这是一个枚举类,枚举类的语法稍微有一些不一样
RUNNING, STUDY, SLEEP; //直接写每个状态的名字即可,最后面分号可以不打,但是推荐打上
}
public enum Status {
RUNNING("睡觉"), STUDY("学习"), SLEEP("睡觉"); //无参构造方法被覆盖,创建枚举需要添加参数(本质就是调用的构造方法)
private final String name; //枚举的成员变量
Status(String name){ //覆盖原有构造方法(默认private,只能内部使用!)
this.name = name;
}
public String getName() { //获取封装的成员变量
return name;
}
}
public static void main(String[] args) {
Student student = new Student("小明", 18, "男");
student.setStatus(Status.RUNNING);
System.out.println(student.getStatus().getName());
}
既然枚举类型是普通的类,那么我们也可以给枚举类型添加独有的成员方法
学习资源来自(147条消息) JavaSE笔记(三)重制版_青空の霞光的博客-CSDN博客
(再次学习时自己做的笔记)