我们知道,在Java中一切皆对象。
那什么是类?什么是对象呢?
类就是一类对象的统称。对象就是这一类具体化的一个实例.
举个简单的例子:我们观察林丹和张继科,发现他们都具有一些相同的属性:都有鼻子眼镜嘴巴等,都具备相同的行为:吃喝拉撒睡。于是我们把具有相同属性和行为的一类对象抽象为类,使用类来描述这类对象的特性。
类是一个抽象的概念,人类,猫类,犬类,它无法具体到某个实体。
对象是某个类的一个实体,比如张三就是人类的一个实体,柯基是犬类的一个实体,由同一个类实例出来的对象具有不同的属性值和行为。
基本语法:
// 创建类
class{
field;//成员属性
method;//成员方法
}
class People{
//成员属性
String name;
int age;
String sex;
//成员方法
void show(){
System.out.println("name = " +name+",age = "+age+",sex = "+sex);
}
}
注意:
1.类的命名使用有意义的大驼峰单词命名方式:
从第一个单词开始首字母就大写,多个单词都是首字母大写。
2.类中的成员变量都有默认值。
类的实例化:
<对象名> = new ();
public class TestDemo {
public static void main(String[] args) {
//实例化对象
People p = new People();
}
}
当产生一个对象后,我们可以用"引用."来访问对象的属性和方法。
成员变量可以在定义时赋初始值,成员变量就地初始化:
注意:
面向对象一共有三大特性:封装,继承和多态
封装:保护性和易用性(封装就有很多种表现形式)
private实现属性和方法的封装只是封装的一种。
我们为什么要对属性和方法进行封装呢?
拿银行卡这个类来说,对于银行卡的卡号,余额,密码这三个属性,如果这三个属性直接暴露在外部,直接就在卡片上贴着,谁都能看见,这是非常不合理的,不能让这些属性通过对象直接就能访问。
再比如,对于汽车这个类来说,车真正发动起来,是需要很多个属性间的相互配合,这些属性对于用户来说,是不可见的,也是用户不关注的,对用户来说,只需要按一键启动来启动汽车,在方法的内部对这些属性进行调配和操作,用户不需要了解汽车内部的属性。
在Java中,所谓的权限修饰符,指的是,你修饰的属性,方法,类,到底可见的范围有多大,一共有四大访问修饰符,可见的范围由小到大依次为:
private < default(不要写这个关键字,啥权限也没有就是default) < protected < public
private:私有的,被private修饰的属性和方法,只在当前类的内部可见,出了类的{},对外部就完全隐藏了,外部不知道有其存在。
public:公共的,公开的,被public修饰的东西,在当前程序(项目)中都是可见的,都是可以使用的。
要想在类的外部去使用这些私有属性,需要使用类提供的getter(取值)和setter(修改值)
到底哪些属性需要提供getter,让外部可见,哪些属性需要提供setter,让外部修改,都要根据这个属性的特征来决定。
private关键字能否修饰一个类?
private关键字不能修饰外部类。
类定义出来就是用来产生对象,让外部使用,private修饰一个类,定义之后外部不可用,无法使用。
构造方法是类中非常特殊的一类方法,使用关键字new实例化对象时,实际上调用的就是该类的构造方法。
构造方法的作用就是产生对象
构造方法语法规则:
1.方法的名称与类名称完全相同
2.构造方法没有返回值声明(不是void)
3.一个类中至少存在一个构造方法,若没有显示定义,编译器会生成一个默认的无参构造。
当类中自定义了构造方法,则默认的无参构造就不再生成。
使用关键字new产生一个对象时,大致分为一下两步:
1.为对象在队中分配空间
2.调用对象的构造方法为对象成员变量赋值。
下面来分析一下代码的执行过程:
public class Test {
public static void main(String[] args) {
Person person = new Person();
}
}
class Person {
private String name;
private int age;
public Person() {
//name = null;age = 0;
System.out.println("Person的无参构造。");
}
}
当调用new person(),其实就是产生了一个Person对象
首先在堆上开辟一块内存空间(大小由该类中成员变量的属性决定)
调用对象的构造方法为所有成员变量赋值
当构造方法调用结束,该对象就初始化完成。
构造方法可以重载,构造方法是为了类中的成员变量赋值的,此时的重载只可能是参数的个数不同。
为啥此时的重载只能是参数个数不同,和类型无关呢?
构造方法是为成员变量赋值,而成员变量的类型在类定义时就指定好了,只是初始化的变量个数不同。
public class Test {
public static void main(String[] args) {
Person person = new Person();
Person person1 = new Person("铭哥");
Person person2 = new Person("铭哥",18);
// 这三个对象构造方法都已经调用结束,初始化完成
}
}
class Person {
private String name;
private int age;
private String sex ;
public Person() {
// name = null,age = 0,sex = null
System.out.println("Person的无参构造。");
}
public Person(String n) {
// name = null,age = 0,sex = null
name = n;
System.out.println("name = " + name);
System.out.println("Person的一个参数的有参构造");
}
public Person(String n,int a) {
// name = null,age = 0,sex = null
name = n;
age = a;
System.out.println("name = " + name + ",age = " + age);
System.out.println("Person的两个个参数的有参构造");
}
}
//运行结果
Person的无参构造。
name = 铭哥
Person的一个参数的有参构造
name = 铭哥,age = 18
Person的两个个参数的有参构造
能否通过实例化对象调用构造方法?
person.Person();
不能调用,对象就是由构造方法产生的,自己又在调用产生自己的构造方法,这是不合理的。
JVM产生对象调用构造方法,对象实例化结束,无法在程序中手动调用构造方法再次实例化对象。
在成员变量定义时就赋初值,程序如何运行?
this表示当前对象的引用
下面来看一段代码:
public class ThisTest {
public static void main(String[] args) {
Student stu1 = new Student("张三",18,"男");
stu1.show();
}
}
class Student {
private String name;
private int age;
private String sex;
public Student(String name,int age,String sex) {
name = name;
age = age;
sex = sex;
System.out.println("Student类的有参构造");
}
public void show() {
System.out.println("name = " + name + ",age = " + age + ",sex = " + sex);
}
}
//运行结果
Student类的有参构造
name = null,age = 0,sex = null
我们发现,当调用有参构造方法时,类中的成员变量并未被赋值,这是为什么呢?
我们知道,构造方法中的三个参数都是形参,这三个参数此时正好与成员变量同名。程序遵循就近匹配原则,编译器会找最近的相同名称的变量。就相当于形参自己指向自己,对类中的成员变量没有任何影响。
如何打破就近匹配原则,从类中找成员变量呢?
我们使用this变量
public class ThisTest {
public static void main(String[] args) {
Student stu1 = new Student("张三",18,"男");
stu1.show();
}
}
class Student {
private String name;
private int age;
private String sex;
public Student(String name,int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
System.out.println("Student类的有参构造");
}
public void show() {
System.out.println("name = " + name + ",age = " + age + ",sex = " + sex);
}
}
//运行结果
Student类的有参构造
name = 张三,age = 18,sex = 男
若不同参数的构造方法之间出现了重复的调用,可以使用this(参数)调用其他的构造方法。
注意:
this调用其他的构造方法必须放在当前构造方法的首行
this调用构造方法不能成“环”
一下代码中,第一个无参构造方法调用了第二个有参构造,第二个有参构造又调用了第一个无参构造,会形成死循环。
this就相当于一面镜子,我当前是通过哪个对象调用的属性或者方法,this就代表谁。当用stu1调用this的时候,this表示的就是stu1这个对象,打印出来的就是stu1的地址。
public class ThisTest {
public static void main(String[] args) {
Student stu1 = new Student();
System.out.println(stu1);
stu1.whoAmI();
System.out.println("-------------------------------");
Student stu2 = new Student();
System.out.println(stu2);
stu2.whoAmI();
}
}
class Student {
private String name;
private int age;
private String sex;
public void whoAmI() {
System.out.println(this);
}
}
//运行结果
Student@4554617c
Student@4554617c
-------------------------------
Student@74a14482
Student@74a14482
代码块:就是使用{}括起来的一段代码。
定义在方法中,用{}括起来的代码块。
public class CodeTest {
public static void main(String[] args) {
{
//普通代码块,出了大括号就不认识
int a = 10;
}
int a = 20;
System.out.println(a);
}
}
//运行结果
20
定义在类中,使用{}括起来的代码块,也叫构造块。
public class CodeTest {
public static void main(String[] args) {
Animal animal = new Animal();
Animal animal2 = new Animal();
}
}
class Animal {
private String name;
{
// 构造块
System.out.println("2.Animal的构造块");
}
public Animal() {
System.out.println("1.Animal的无参构造");
}
}
//运行结果
2.Animal的构造块
1.Animal的无参构造
2.Animal的构造块
1.Animal的无参构造
构造块直接定义在类中,不加任何修饰符,它优先于构造方法执行,有几个对象产生就调用几次构造块。
定义在类中,使用static修饰的代码块,在类加载的时候执行一次
public class CodeTest {
public static void main(String[] args) {
Animal animal1 = new Animal("小狗");
Animal animal2 = new Animal();
}
}
class Animal {
private String name;
static {
// 静态块
System.out.println("3.Animal的静态代码块");
}
{
// 构造块
System.out.println("2.Animal的构造块");
}
public Animal() {
System.out.println("1.Animal的无参构造");
}
public Animal(String name) {
this.name = name;
System.out.println("1.Animal的有参构造");
}
}
//运行结果
3.Animal的静态代码块
2.Animal的构造块
1.Animal的有参构造
2.Animal的构造块
1.Animal的无参构造
静态代码块在类加载的时候执行一次,与对象无关,无论产生多少对象,静态代码块只在类加载的时候执行一次。
主类中的静态代码块优先于主方法执行,JVM要执行主方法,首先得加载主类,主类一加载,静态块就执行了。