文章目录
面向对象基础
类:数据类型(包含属性和行为)
对象:具体的实例
对象的创建与访问属性
public class cat{
public static void main(String[] args) {
cat cat1=new cat();//new cat()是创建的对象空间,cat1是引用的名字
cat1.name="a";
cat1.age=3;
cat1.color="blue";
System.out.println("cat1"+cat1.name+cat1.age+cat1.color);
}
}
class cat{ //属性,field(字段),成员变量
String name;
int age;
String color;
}
克隆对象
public class method{
public static void main(String[] args) {
Person p=new Person();
p.name="fengwenjie";
p.age=19;
Tools tool=new Tools(); //创建调用方法的对象
Person p2=tool.copy(p); //克隆p对象,具有相同属性
System.out.println(p.name+" "+p.age);
System.out.println(p2.name+" "+p2.age);
}
}
class Person{
String name;
int age;
}
class Tools{
public Person copy(Person p){ //克隆对象
Person p2=new Person();
p2.name=p.name;
p2.age=p.age;
return p2;
}
}
对象内存布局
public static void main(String[] args) {
Cat cat=new Cat();
cat.name="小白";
cat.color="白色";
}
class Cat{
String name;
String color;
}
方法
1.通过“."进行访问
2.方法使用:new一个对象,通过.调用方法
3.方法不能嵌套定义!
4.同一个类中的方法直接调用
5.跨类调用方法,通过new一个对象调用
6.方法调用机制:
执行方法时,开辟独立的占空间,执行完毕,返回调用者
7.访问修饰符:public private protected 默认
方法实例:返回数组的函数调用
public class method{
public static void main(String[] args) {
Tools tool=new Tools();
int []res=tool.getSumAndSub(3,2);//数组接收
System.out.println(res[0]+" "+res[1]);
}
}
class Tools{
public int[] getSumAndSub(int n1,int n2){
int []resArr=new int [2];
resArr[0]=n1+n2;
resArr[1]=n1-n2;
return resArr;
}
}
传参机制
传参(parameter)机制:
1.基本数据类型:值拷贝,实参不改变
2.引用类型(数组,对象):地址拷贝
方法重载
方法名相同,形参个数不同
class Methods{ //返回不同类型的最值
public int max(int n1,int n2){
return n1>n2?n1:n2;
}
public double max(double n1,double n2){//两数最大值
return n1>n2?n1:n2;
}
public double max(double n1,double n2,double n3){//三数最大值
double max1=n1>n2?n1:n2;
return max1>n3?max1:n3;
}
}
可变参数
一个参数列表中只能包含一个可变参数
可变参数求和
public class overload{
public static void main(String[] args) {
Methods method=new Methods();
//实参可以为数组
int[]arr={1,2,3};
System.out.println(method.sum(arr));
System.out.println(method.sum(1,2,3,4));
}
}
class Methods{
public int sum(int...nums){ //接受可变参数
int res=0;
for(int i=0;i<nums.length;i++)
res+=nums[i];
return res;
}
}
可变参数和普通参数一起时,可变参数必须放在最后
public class overload{
public static void main(String[] args) {
Methods method=new Methods();
System.out.println(method.showscore("milan",90.1,60));
System.out.println(method.showscore("jack",90.1,60,80,88));
}
}
class Methods{
public String showscore(String name,double...score){
double totalscore=0;
for(int i=0;i<score.length;i++)
totalscore+=score[i];
return name+"成绩总分为"+totalscore;
}
}
作用域
局部变量:
定义在成员方法中的变量,只在方法体中使用
全局变量(属性):
作用域为整个类体
赋值:
局部变量必须赋值后使用
全局变量(属性)默认赋值
属性和局部变量可以重名,就近原则访问
生命周期:
执行完方法局部变量被销毁,属性任然可以使用
使用范围:
属性可以通过调用对象跨类访问,局部变量只能在本类中使用
修饰符:
全局变量可以加,局部不能加
构造方法/构造器
1.完成对新对象的初始化,不是创建对象
2.无返回值,方法名与类名相同
3.如果没定义,则系统会自动生成默认构造器,通过javap xx.class反编译
4.自己定义的构造器会覆盖默认构造器,不能再使用默认构造器了
public class constructor{
public static void main(String[] args) {
Person p1=new Person("smith",90);//第一个构造器
Person p2=new Person("jack")//第二个构造器;
Person p3=new Person();//无参构造器
System.out.println(p1.name+" "+p1.age);
System.out.println(p2.name+" "+p2.age);
System.out.println(p3.name+" "+p3.age);//name为默认null
}
}
class Person{
//直接初始化人的姓名和年龄
String name;
int age;
//双参构造器
public Person(String pname,int page ){
name=pname;
age=page;
}
//单参构造器
public Person(String pname){
name=pname;
}
//无参构造器
public Person(){
age=18;
}
}
对象创建流程
class Person{
int age;
String name;
Person(String n,int a)[
name=n;
age=a;
}
Person p=new Person("小倩",20);
1.首先加载Person类
2.,在堆中创建对象
3.默认赋值,再显示初始化,遇到构造器再替换信息
4.将对象在堆中的地址返回给p
This关键字
哪个对象调用,this就代表哪个对象dog1和this的hashCode相同
Dog dog1=new Dog("jack",3);)
class Dog{
String name;
int age;
public Dog(String name,int age){
this.name=name;//当前对象属性
this.age=age;
}
比较两人的年龄和名字
Person p1=new Person('jack',19);
Person p2=new Person('tom',18);
System.out.println(p1.compareTo(p2));//p1相当于this
class Person{
String name;
int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public boolean compareTo(Person p){
return this.name.equals(p.name)&&this.age.equals(p,age);
}
更改价格
public class homework{
public static void main(String[] args) {
T t=new T();
Book book=new Book("笑傲江湖",300);
book.info();
book.upDataPrice();
book.info();
}
}
class Book{
String name;
double price;
public Book(String name,double price){//构造器
this.name=name;
this.price=price;
}
public void upDataPrice(){//更新价格
if(this.price>150){
this.price=150;
}
else this.price=price;
}
public void info(){//打印
System.out.println("书名"+this.name+"价格"+this.price);
}
}
章末练习
返回数组最大值
public class homework{
public static void main(String[] args) {
double []arr={1,2,4,5,7};
T t=new T();
System.out.println(t.max(arr));
}
}
class T{
public double max(double []arr){
double max=arr[0];
for(int i=1;i<arr.length;i++){
max=Math.max(max,arr[i]);
}
return max;
}
}
返回字符串在数组中的下标
public class homework{
public static void main(String[] args) {
T t=new T();
String[] strs={"jack","tom","mary"};
int index=t.find("tom",strs);
System.out.println(index);
}
}
class T{
public int find(String findstr,String[]strs){
for(int i=0;i<strs.length;i++){
if(findstr.equals(strs[i]))
return i;
}
return -1;
}
}
面向对象提高
访问修饰符
1 同一个类中都可以访问
2.相同包下除private都可以访问
3.不同包下只能访问public
修饰属性和方法
只有默认和public才能修饰类
封装
隐藏细节,验证数据安全
封装步骤:
1.将属性私有化(private属性)
2.创建set和get方法
继承
不同类的方法可以共用,代码复用
1.子类继承所有属性和方法,不能直接访问父类的私有属性和方法,要父类提供公共(public)方法
2.调用子类构造器时,会默认调用父类构造器
3.父类如果没有提供无参构造器,则必须通过子类
用super指定构造器完成对父类的初始化
4.super放在构造器第一行,super只能在构造器中使用
5.super(),this()只能用一个,放在第一行
继承内存布局:
从顶级父类开始加载类,在堆中分别分配空间保存各个类的属性
访问顺序:
1.如果子类有就可以访问的属性/方法,则直接返回子类;否则向上查找父类的可以访问属性/方法。
2.如果找到但是不能访问,则报错
构造器调用顺序:this,super跳转
class A{
A(){System.out.println("a");}
class B extends A{
B(){this("abc");//调用下面的含参构造器
System.out.println("b";}
B(String name){
//调用默认的父类构造器
System.out.println("b name");)
//B b=new B() 输出 a b name b
super
父类的引用,可以访问父类的非private属性/方法/构造器super.xx
调用方法的形式:
call();
this.call();
super.call();//直接从父类中查找
super与this的比较
方法重写
子类方法的名字,返回类型,参数相同,则子类覆盖父类方法
public class Animal{
public void cry(){}
}
public class Dog extends Animal{
public void cry(){}//重新父类方法
}
返回类型相同或者子类返回类型包含于父类
public Object m{}
public String m{}//String属于Object,重写m方法
子类不能缩小父类方法的访问权限
public>protected>默认>private
//父类
public String say(){return name+" "+age;}
//子类
public String say(){return super.say+id+score;}
方法重载与重写
多态
方法或对象具有多种形态,建立在封装和继承
1.方法重载,方法重写都有体现
2.对象多态
2.1一个对象的编译类型和运行类型可以不同
2.2编译类型在定义时确定,不能再改变,运行类型可以改变,通过getClass()查看
2.3编译类型看=左边,运行类型看=右边
Animal animal=new Dog();
animal.cry();//调用Dog类方法
Animal animal=new Cat();
animal.cry();//调用Cat类的方法
/*向上转型
animal可以调用父类的所有成员,但是不能调用子类的特有成员,
运行效果取决于子类具体实现,从子类开始查找方法*/
//向下转型
Animal animal=new Cat();
Cat cat=(Cat)animal;//强转为Cat类的引用
cat.catchMouse();
Dog dog=(Dog)animal;//animal并不指向Dog,因此不能强转为dog
动态绑定机制:
1.调用对象方法时,方法会和运行类型绑定
2.调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
多态数组
根据instanceof判断子类类型,调用特定方法
Person[]person=new Person[2];
person[0]=new Student("jack",20);
Person[1]=new Teacher("marry",30);.
for(int i=0;i<person.length;i++){
//调用子类重写的方法say()
System.out.println(person[i].say())
//调用子类特有方法
//instanceof判断运行类型是否为某类型或其子类型
if(person[i] instanceof Student){
Student student=(Student)person[i];//向下转型
student.study();
//(Student)person[i].study();
}else if(person[i] instanceof Teacher){
(Teacher)person[i].teach();
}
}
多态数组排序
//对象创建
Person[]per=new Person[3];
per[0]=new Person("jack",19,"cooking");
per[1]=new Person("marry",20,"houseworking");
per[2]=new Person("peter",22,"play");
//按照对象的年龄排序
for(int i=0;i<per.length;i++)
for(int j=0;j<per.length-1-i;j++)
if(per[j].age>per[j+1].age){//比较属性
Person temp;//交换的是Person类的对象
temp=per[j];
per[j]=per[j+1];
per[j+1]=temp;
}
Object类
equals方法
不可以判断基本数据类型,可以判断两个对象是否相等
String string1 = new String("abc");
String string2 = new String("abc");
System.out.println(string1==string2);//false
System.out.println(string1.equals(string2));//true
方法重写
public class jdk {
public static void main(String[] args) {
//比较两个对象的多个属性
Person person1 = new Person("小明",18,'男');
Person person2 = new Person("小明",18,'男');
System.out.println(person1.equals(person2));
}
}
class Person {
private String name;
private int age;
private char gender;
//重写equals方法
public boolean equals(Object obj) {
if (this == obj) {//同一个对象返回true
return true;
}
if (obj instanceof Person) {
Person p = (Person) obj;//得到属性
return this.name.equals(p.name) &&//equals(String)
this.age == (p.age) &&//基本类型用==
this.gender == (p.gender);
} else {
return false;
}
}
}
HashCode
不同对象的哈希值不同,主要根据地址
A a=new A();
A b=new A();
a.hashCode()!=b.hashCode();
toString
输出属性
返回值为类全名+哈希值的16进制值
直接输出对象会默认调用toString();
finalize
对象销毁前会自动调用finalize方法,回收没有引用的对象,释放资源
Car bm=new Car("宝马");
bm=null;//Car无引用被回收
System.gc();//运行垃圾回收器
面向对象进阶
静态
静态变量
1.static变量同类所有对象共享
2.静态变量在类加载时生成,不用创建对象实例
访问:
类名.静态变量名
不能通过此方法访问非静态变量
A.name;//类名.变量名访问
A a=new A();
a.name;//类实例访问
class A{public static String name="feng";}
静态方法/类方法
1.不创建实例也可以调用方法,则做成静态方法,直接通过类名调用
2.静态方法不能使用this/super,不能调用非静态方法
3.普通方法既可以方法静态也可以访问非静态方法
4.静态变量不会受构造器影响,只有在new创建实例时才会调用构造器
main方法
1.main方法由虚拟机调用
2.不用创建对象,因此是static
3.java 执行程序 参数1 参数2,传入[]args数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E2mIFw0u-1674124788566)(en-resource://database/2203:1)]
4.main中只能直接访问static成员,否则必须创建实例才能访问非静态成员
代码块
提高构造器中代码复用性,代码块调用顺序优先于构造器
1.静态代码块随类加载而只会执行一次
2.类被加载的时机:
3.普通代码块每在对象创建(new)时就调用一次,调用静态变量才会调用静态代码块
4.创建一个对象时,一个类的调用顺序
1)先调用静态代码块和完成静态属性初始化
2)再调用普通代码块和完成普通属性的初始化
3)最后调用构造器
5.构造器先调用父类构造器,再调用本类代码块,最后执行自身内容
6.创建子类时的调用顺序
7.静态代码块只能调用静态成员,普通代码块能调用所有成员
单例设计模式
饿汉式–创建对象不一定使用
1)构造器私有化
2)内部创建static对象
3)提供public方法访问
girlfriend instance=grilfriend.getInstance();
class girlfriend{
private String name;
private static girlfriend gf=new girlfiend("marry");
private girlfriend(String name){
this.name=name;
}
public static girlfriend getInstance(){
return gf;
}
懒汉式—需要使用才创建
1)构造器私有化
2)定义static对象
3)提供公共访问方法,判断是否需要创建对象
class Cat{
private String name;
private static Cat cat;
private Cat(String name){
this.name=name;
}
public static Cat getInstance(){
if(cat==null)//未创建时才创建
cat=new Cat("herry");
return cat;//返回上次创建的对象
}
final
1)不希望被继承
final class Person{}
2)阻止子类重写父类方法
可以继承,不能重写
public final void hi(){}
3)属性/变量不能修改
常量必须赋值:
private final double PI=3.14;//定义
pulic Circlle(double radius){//构造器
this.radius=radius;
PI=3.14;
}
{PI=3.14}//代码块
静态属性不能在构造器中赋值,而是要在静态代码块中赋值
public final double TAX_RATE=0.09;
final double NUM=0.01;
4)final类中不用声明final方法
5)final+static阻止类加载
//主方法调用num时不会加载代码块
public final static int num=1000;
static{};//不会被调用
抽象类
父类方法不确定,设计为抽象方法,没有实现的方法,通过子类实现
abstract class Animal{//声明抽象类
public abstract void eat();//抽象方法
}
1)抽象类不能实例化,不能new
2)抽象类可以没有抽象方法,含抽象方法的必须是抽象类
3)abstract只能修饰类和方法
4)继承抽象类必须实现所有抽象方法,除非自己也声明为抽象类
5)抽象方法不能使用private,final,static,因为与继承相违背
模板:
abstract class Template{//模板类
public abstract void job();//抽象方法
public void Calculate(){
long start=System.currentTimeMillis();
job();
long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
class A extends Template{
public void job(){//必须重写job()
long num=0;
for(long i=0;i<100000;i++){
num+=i;
}
}
}
public class Main{
public static void main(String[] args) {
A a=new A();
a.Calculate();//调用
}
}
抽象方法例子
abstract class Animal{
public abstract void shout();
}
class Dog extends Animal{
public void shout(){
System.out.println("狗叫");
}
}
class Cat extends Animal{
public void shout(){
System.out.println("猫叫");
}
}
public class hhh{
public static void main(String[] args) {
Animal dog=new Dog();
Animal cat=new Cat();
dog.shout();
cat.shout();
}
}
接口
接口就是一些没有实现的方法,封装到一起,到需要使用某个类时,在根据需要实现具体的方法
interface 接口名{//public或者默认
//属性
//方法(抽象方法/default/static)
}
class A implements 接口{
//必须实现接口所有方法
//alt+enter
}
class C implements A,B{}//实现多个接口
interface IB{
int n1=10;
//实际是public static final int n1=10
}
//接口继承
interface A{}
interface B extends A{}
//可以通过A.a B.a访问属性
interface A{int a=10}
class B implements A{}
接口是对单继承机制的补充
子类扩展功能可以通过接口实现
class LittleMonkey extends Monkey implements Fish,Bird{}
接口数组
public static void main(String[] args) {
Usb[]usbs=new Usb[2];
usbs[0]=new phone();
usbs[1]=new camera();
for(int i=0;i<usbs.length;i++){
usbs[i].work();
//类型判断,实现特定方法
if(usbs[i] instanceof phone){
((phone)usbs[i]).call();
}
}
}
interface Usb{
work();
}
class phone implements Usb{
public void work(){}
public void call();//特有方法
}
class camera implements Usb{
public void work(){}
}
interface Vehicles{
public void work();
}
//实现接口不要忘了implements!!
class Horse implements Vehicles{
public void work(){
System.out.println("一般使用马");
}
}
class Boat implements Vehicles{
public void work(){
System.out.println("过河用船");
}
}
//get()方法
class Factory{
private static Horse horse=new Horse();//饿汉式
public static Horse getHorse(){
return horse;//每次不用new一个Horse();
}
public static Boat getBoat(){
return new Boat();
}
}
class Person{
private String name;
private Vehicles vehicles;
public Person(String name,Vehicles vehicles){
this.name=name;
this.vehicles=vehicles;
}
//封装
public void PassRiver(){
//vehicles不是船时就用船
if(!(vehicles instanceof Boat)){
vehicles=Factory.getBoat();
}
vehicles.work();
}
public void Common(){
//不是马就用马
if(!(vehicles instanceof Horse)){
vehicles=Factory.getHorse();
}
vehicles.work();
}
}
public class Main{
public static void main(String[] args) {
Person person=new Person("唐僧",new Horse());
person.PassRiver();
person.Common();
}
}
内部类
类成员:属性,方法,构造器,代码块,内部类
局部内部类
1)不能添加修饰符,可以用final
2)作用域在方法体中
3)可以访问外部类成员,outer.this.xx;
class outer{
private int n1=10;
public void f(){
class inner{//方法体中
private int n1=20;
//调用外部的n1 outer.this.n1
public void f2();}
}
inner in=new inner();//创建内部类对象访问方法
in.f2();
}
匿名内部类
//通过对象调用
Person p=new Person{
void do{};
};
p.do();
//直接调用
new Person(){
void do();//重写Person()的do()方法
}.do();
传递接口的匿名内部类
interface Bell{
void ring();
}
class CellPhone{
public void alarm(Bell bell){
bell.ring();
}
}
public class hhh{
public static void main(String[] args) {
CellPhone phone=new CellPhone();
phone.alarm(new Bell(){
public void ring(){//public权限
System.out.println("起床了");
}
});
}
}
应用:传入实现接口的方法参数
public class hhh{
public static void main(String[] args) {
Phone phone=new Phone();
//传入实现接口的方法的参数
phone.test(new Calcu(){
public double work(double n1,double n2){
return n1+n2;
}
},1,2);
}
}
interface Calcu{
public double work(double n1,double n2);
}
class Phone{
//传入接口参数
public void test(Calcu calcu,double n1,double n2){
double res=calcu.work(n1,n2);
System.out.println(res);
}
}
成员内部类
不在外部类的方法中,直接定义在外部类成员位置上
class outer{
class inner{ //成员内部类
void say();
}
public void test(){//作为调用内部类的桥梁
inner in=new inner();
in.say();
}
}
class Main{
outer ot=new outer();
ot.test();//调用成员内部类方法
}
public class Main{
public static void main(String[] args) {
Car car=new Car(60);
//通过get方法调用
car.getAir().flow();
}
}
class Car{
private double temper;
public Car(double temper){
this.temper=temper;
}
class Air{
public void flow(){
if(temper>40){
System.out.println("吹冷气");
}else if(temper<0){
System.out.println("吹暖气");
}else{
System.out.println("温度正常");
}
}
}
//返回得到内部类对象的方法
public Air getAir(){
return new Air();
}
}
静态内部类
class outer{
private static int n1=10;
private static void work();
static class inner{ //静态内部类
static int n1=20;
public void say(){
System.out.println(n1);//就近原则访问静态属性
System.outprintln(outer.n1);//outer.xx,访问外部属性
work();//调用静态方法
}
}
}
class Main{
public static void main(String[] args) {
outer.inner in=new outer.inner(); //静态内部类对象
in.say();//其他类访问内部类
}
}