Java基础知识(持续更新中...)
1.成员变量:全局变量/字段(Field),不要称之为属性(错误)直接定义在类中,方法外面
1.类成员变量 使用static修饰的变量
2.实例成员变量 没用使用static修饰的变量
局部变量:除了成员变量其他的变量都是局部变量
1.方法体中的变量
2.方法的形参
3.代码块中的变量 (一对{})
----------------------------------------------------------------------------------------------------------------------------
变量的初始化:只有初始化了才会在内存中开辟空间
成员变量:默认是有初始值的,数值类型的默认都是0,布尔类型默认是false ,char类型默认是
'\u0000'(表示空)
局部变量:没有初始值,所以必须先初始化才能够使用。
PS:成员变量可以先使用而后定义。局部变量则不可以。
----------------------------------------------------------------------------------------------------------------------------
变量的作用域:指的是变量的存在范围,只有在这个范围内,程序代码才能够访问它,当一个变量被定义时,它的作用域就确定了。
成员变量:整个类中都有效
局部变量:从开始定义的位置到紧跟着的花括号为止。(例如形参是整个方法中)
成员变量可以先使用后定义。
----------------------------------------------------------------------------------------------------------------------------
变量的生命周期:是指一个变量从被创建和分配内存空间开始到该变量被销毁并清楚其所占的内存空间的过程
变量的作用域决定了变量的生命周期,说明作用域不同,生命周期就不一样。
----------------------------------------------------------------------------------------------------------------------------
2.JVM的内存模型:
Java文件编译后生成.class的字节码文件。
JVM的功能是:它将.class字节码文件编译成机器语言,以便机器识别
3.关于Native接口
https://baike.baidu.com/item/native/13128691
4.对象的比较操作、对象的生命周期和匿名对象:
== 运算符:
对于基本数据类型而言是比较的值, 对于引用类型而言是比较的内存中的地址值。
每次使用new关键字都会在堆上开辟一块新的内存空间,内存空间不同,地址就不一样。
Object类中的equals可以只比较内容是否相同而不比较地址。
对象的生命周期:当使用new关键字时对象出生,当堆中的对象没有被任何变量所引用时,此时对象就成了垃圾,等着被垃圾回收器(GC)自动回收。
当被回收的时候,对象就会被销毁。
匿名对象:没有名称的对象,创建对象后没有赋值给任何一个变量。
如:new student();匿名对象只是在堆中开辟了一块内存空间,但是没有把该地址赋值给任何变量。
因为没有名称,匿名对象只能使用一次。
如:new student().name = "amos";
一般的把匿名对象作为方法的实参传递
5.static修饰符的特点:
static修饰符表示静态的,可修饰字段、方法和内部类,其修饰的成员属于类,也就是说static修饰的资源属于类级别,而不是对象级别。
static真正的作用:用来区别字段、方法、内部类,初始化代码块是属于对象还是属于类本身。
static修饰符的特点:
1):static修饰的成员(字段(类成员或者称为全局变量)/方法)随着所在类的加载而加载。
当JVM把字节码加载进JVM的时候,static修饰的成员已经在内存中存在了
2):优先于对象的存在
对象是我们手动通过new关键字创建出来的。
3):static修饰的成员被该类型的所有对象所共享。
根据该类创建出来的任何对象都可以访问static成员。(如狗天生就会吃屎)
表面上通过对象去访问static成员,其本质仍然是使用类名访问,和对象没有任何关系。
4):直接使用类名访问static成员
因为static修饰的成员直接属于类,不属于对象,所以可以直接使用类名访问static成员。
static修饰的变量存储在JVM内存模型中的方法区(见上)作为共享数据。
static方法访问的变量需要用static修饰
6.package最佳实践
static import:(这种方式不好,开发中不用)
7.JavaBean规范:
JavaBean是一种JAVA语言写成的可重用组件(类)
必须遵循一定的规范:
1):类必须使用public修饰
2):必须保证有公共无参构造器,即使手动提供了带参数的构造器,也得提供无参数构造器。
3):包含了属性的操作手段(给属性赋值,获取属性值)。
分类:
1):复杂:UI,比如Button,Panel,Window类
2):简单:domain,dao,service组件,封装数据,操作数据库,逻辑运算符
(封装有字段,并提供getter/setter)
成员:
1):方法:Method
2):事件:event
3):属性:property
-------------------------------------------------------------------------------------------------------------
属性:
1):attribute:表示状态,Java中没有该概念,很多人把字段称之为属性(attribute),不要把成员变量叫做属性,属性和字段是完全不同的概念。
2):property:表示状态,但是 不是字段,是属性的操作方法(getter/setter)决定的,框架中使用的大多是property属性。
--------------------------------------------------------------------------------------------------------------
getter和setter方法(必须用public 修饰):
setter方法:仅仅用于给某个字段设置需要的存储值。
getter方法:仅仅用于获取某一个字段存储的值。
如果操作的字段是boolean类型的,此时不应该叫做getter方法而是is方法,把getName改为isName。
在JavaBean中有属性这个概念,只有标准情况下字段名和属性名才相同。
如:字段:private String name;
setter方法:setName --> 属性为name(标准情况,此时字段名=属性名)
setName1--> 属性为name1(非标准情况,此时字段名!=属性名)
8.this关键字
this表示当前对象。
this使用位置:
1).在构造器中:就表示当前创造的对象
如:第二行user的构造器中,第13和第16行创建u1和u2时调用了构造器,两次调用构造器 this分别指代u1和u2
1 class user(){ 2 user(){//user的构造器
System.out.println(this);
}
3 private String name; 4 private int age; 5 //省略 setter&getter 6 public void show(){//打印当前对象的name和age 7 System.out.println(this.name + ", " + this.age); 8 } 9 public thisDemo{ 10 11 public static void main(String[] args){ 12 user u1 = new user(); 13 u1.setName("lucy"); 14 u1.setAge(18); 15 u1.show();//显示出u1自己的name和age 16 user u2 = new user(); 17 u2.setName("bob"); 18 u2.setAge(19); 19 u2.show();//显示出u1自己的name和age 20 21 } 22 }
2).在方法中
哪一个对象调用this存在this 的方法,this就指代哪一个对象
如第16和第19行
this方法的使用:
①解决成员变量和参数(局部变量)之间的二义性,必须使用
②同类中的实例方法间互相调用(此时可以省略this但是不建议省略)
③将this作为参数传递给另一个方法
④将this作为方法的返回值(链式方法编程)
⑤构造器重载的互调,this(参数)必须写在构造方法的第一行。
⑥static不能和this一起使用
当字节码被加载进JVM,static成员已经存在了。
但是此时对象没有创建,没有对象就没有this
②的举例说明
⑤的补充说明:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
9.super关键字
this:当前对象,谁调用this方法谁就是当前对象。
super:当前对象的父类对象
1 package testSuper; 2 3 class bird{ 4 public void fly() { 5 System.out.println("I can fly!"); 6 } 7 } 8 9 class penguin extends bird{ 10 public void fly() { 11 System.out.println("我不会飞,但是我会唱"); 12 } 13 public void say() { 14 System.out.println("啊啊啊,我会唱"); 15 /* 16 super.fly()表示调用父类中的fly方法 17 如果不加super则表示调用本类中的fly方法 实质上就是this.fly(); 18 */ 19 super.fly(); 20 } 21 } 22 23 public class TestSuper { 24 public static void main(String[] args) { 25 penguin p = new penguin(); 26 p.say(); 27 } 28 }
隐藏的含义:
①满足继承的访问权限下,隐藏父类的静态方法,若子类定义的静态方法的签名(方法名+形参列表)和超类中的静态方法的签名相同,那么此时就是隐藏父类方法。注意仅仅是静态方法。
注意隐藏和覆盖的区别:只有静态方法时才是隐藏。
②满足继承的访问权限下,隐藏父类字段:若子类中定义的字段和父类中的字段名相同(无论类型),此时就是隐藏父类字段,此时只能通过super访问被隐藏的字段。
③隐藏本类字段:若同类中某局部变量名和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段
由此推出super关键字的使用场景:
1):可以使用super解决子类隐藏了父类的字段:(该情况我们一般不讨论,因为破坏封装)
1 class A{ 2 public String name = "superclassName"; 3 } 4 class B extends A{//B继承自A 5 public int name = 18; 6 public void doWork(){ 7 System.out.println(name);//结果为18 8 System.out.println(super.name);//结果为superclassName 9 } 10 }
2):在子类方法中调用了父类被覆盖的方法,引出super的例子,此时必须使用super。
3):在子类构造器中,调用父类构造器,此时必须使用super语句:super(实参)
super、this和static都不能共存。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
10.子类初始化过程:
①在创建子类对象之前会先创建父类对象。(必须先有父类对象,然后才有子类对象)
②在创建子类对象的执行顺序是:先进入子类构造器,然后在构造器中会先调用父类构造器(创建父类对象),再执行子类构造器代码。
③如果父类不存在可以被子类访问的构造器,则不存在子类。
④如果父类中没有无参数构造器,子类必须显式去调用父类中带参数的构造器。
1 package subclassInit; 2 3 class Animal{ 4 private String name; 5 private int age; 6 Animal() { 7 System.out.println("Animal的构造器"); 8 } 9 } 10 11 class Fish extends Animal{ 12 private String color; 13 Fish() {
//super(); 隐式的super()构造器 14 System.out.println("Fish的构造器"); 15 } 16 } 17 public class InitDemo { 18 public static void main(String[] args) { 19 Fish f = new Fish(); 20 } 21 }
输出结果为:Animal的构造器
Fish的构造器
在第14行之前其实存在一个隐式的super();用于调用父类的无参数构造器
子类显式的调用父类中构造器的例子:
1 class Animal{ 2 private String name; 3 private int age; 4 public Animal(String name, int age) { 5 this.name = name; 6 this.age = age; 7 System.out.println("Animal的构造器"); 8 } 9 } 10 //问题类Fish中没有字段name和age显然只能通过调用父类的参数构造器来获取。 11 class Fish extends Animal{ 12 private String color; 13 Fish(String name, int age, String color) { 14 super(name, age);//调用父类构造器的这句话必须作为子类构造器的第一句话 15 this.color = color; 16 System.out.println("Fish的构造器"); 17 } 18 }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
11.多态
<1>什么是多态?
对象具有多种形态,对象可以存在不同形式。
举个栗子引出多态的概念:
1 package polymorphicTest; 2 3 class Animal{ 4 public void eat() { 5 System.out.println("吃普通的食物"); 6 } 7 } 8 9 class Dog extends Animal{ 10 public void eat() { 11 System.out.println("吃肉骨头"); 12 } 13 } 14 15 class Cat extends Animal{ 16 public void eat() { 17 System.out.println("吃鱼"); 18 } 19 } 20 21 public class Demo { 22 23 public static void main(String[] args) { 24 Dog dog = new Dog(); 25 Cat cat = new Cat(); 26 dog.eat(); 27 cat.eat(); 28 } 29 30 }
当主方法中24-27行变成下面时多态就产生了:
24 Animal dog = new Dog();
25 Animal cat = new Cat();
26 dog.eat();
27 cat.eat();
<2>多态是如何产生的?
对象dog有两种类型:
编译类型:声明对象变量的类型,Animal,表示把对象看成什么类型。
运行类型:对象的真实类型,Dog。运行类型也称作对象的真实类型。
编译类型必须是运行类型的父类或者二者相同
Animal a = null; a = new Dog();//a此时表现出Dog形态 a = new Car();//a此时表现出Cat形态
<3>多态的前提:可以是继承关系(类和类之间)/也可以是实现关系(接口和实现类),在开发中多态一般都指第二种
<4>多态的特点:
把子类对象赋给父类变量,在运行时期会表现出具体的子类特征(调用子类的方法)
<5>多态的好处
当把不同的子类对象都当做父类类型来看待,可以屏蔽不同子类对象之间的实现差异,从而写出通用的代码达到通用编程,以适应需求的不断变化。
举个栗子:
package polymorphicTest; class Animal{ public void eat() { System.out.println("吃普通的食物"); } } class Dog extends Animal{ public void eat() { System.out.println("吃肉骨头"); } } class Cat extends Animal{ public void eat() { System.out.println("吃鱼"); } } /**以下为修改 新增的内容**/ class Person{ public void feed(Animal a) { System.out.println("feed...."); a.eat(); } } public class Demo { public static void main(String[] args) { Dog dog = new Dog(); Cat cat = new Cat(); Person p = new Person(); p.feed(dog); p.feed(cat); } }
12.final修饰符
<1>为什么使用final修饰符
继承关系最大的弊端就是破坏封装:子类能访问父类的实现细节,而且可以通过方法覆盖的形式修改实现细节。
<2>多个修饰符之间是没有先后顺序的:public static final
<3>final的本身含义是最终的,不可改变的,它可以修饰非抽象类,非抽象方法和变量。注意:构造方法不能使用final修饰,因为构造方法不能被继承,肯定是最终的。
final修饰的类:表示最终类,该类不能再有子类。
只要满足以下条件就可以把一个类设计成final类:
① 某类不是专门为继承而设计的
② 出于安全考虑,类的实现细节不许改动,不准修改源代码。
③ 确信该类不会被拓展
面试题:列举5个Java中内置的使用final修饰的类
八大基本数据类型包装类和String等
final修饰的方法:最终的方法,该方法不能被子类覆盖
什么时候的方法需要使用final修饰
1:在父类中提供的统一的算法骨架,不准子类通过方法覆盖来修改,此时使用final修饰。模板方法设计模式
2.在构造器中调用的方法(初始化方法)此时用final修饰
final修饰的变量:最终的变量,常量,只能赋值一次,不能再赋值。
全局静态常量:public static final 修饰的变量,直接使用类名调用即可
final修饰引用类型变量:表示该变量的引用地址不能变,而不是引用地址里面的内容不能变
final是唯一一个可以修饰局部变量的修饰符(局部内部类)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
13.基本类型包装类和装箱封箱操作
装箱:把基本类型数据转成对应的包装类对象。(PS:在集合中只能使用包装类型不能够使用基本数据类型)
拆箱:把包装类对象转换成对应的基本数据类型的数据
从Java5开始提供了自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)功能
自动装箱:可把一个基本类型变量直接赋值给对应的包装类对象
自动拆箱:允许把包装类对象直接赋值给对应的基本数据类型变量。
举个栗子:
//装箱:基本数据类型-->包装类 //方式一 Integer num1 = new Integer(17); //方式二 Integer num2 = Integer.valueOf(17);//推荐有缓存 //拆箱:包装类-->基本数据类型 int num3 = num1.intValue(); //自动装箱技术 基本数据类型直接赋值给包装类对象 Integer num4 = 5; //自动拆箱: int num5 = num4;
自动装箱和自动拆箱也是一个语法糖/编译器级别新特性。
在底层仍然是手动拆箱和装箱。
但是:装箱操作使用的是Integer.valueOf的方式而不是new Integer.
switch支持的数据类型:byte,short,char,int 也支持对应的包装类。
支持包装类的原因是:switch中会对包装类进行手动拆箱操作。
解释:Object obj = 17;
1)自动装箱 Integer i = 17;
2):引用的自动类型转换,把子类对象赋给父类变量:Object obj = i;
Object 可以接受一切数据类型的值。
object数组:Object[]该数组可以装一切数据类型
Object[] = {"A",123,false};
包装类的常用操作方法:
1):包装类中的常量
MAX_VALUE/MIN_VALUE/SIZE(在内存中存储占多少位)/TYPE(对应的基本类型)
举个栗子:Integer.MAX_VALUE, Integer.SIZE;
2):包装类构造器
Integer(5); Double(5.0);
3):基本类型和包装类型的转换(拆箱和装箱操作)
4):String和基本类型/包装类型之间的转换操作
1>把String类型转换为包装类
方式一:Integer num1 = Integer.valueOf("123");
Double num2 = Double.valueOf("123.456");
方式二: Integer num3 = new Integer("123");
2>把包装类对象转换为String
String str = num1.toString();
3>把基本数据类型转换为String
String str = 5 + "";
4>把String转换为基本类型
String input = "123456";
int num = Integer.parseInt(input);
5>:Boolean b = new Boolean("b")//false
只认可true/TRUE为true,其他都是false;
包装类的缓存设计:
int和Integer的区别:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
14.正则表达式
举个栗子:
1 public static void main(String[] args) { 2 //判断字符串是不是由纯数字组成 3 String str = "123456a"; 4 String regex = "\\d*"; 5 System.out.println(str.matches(regex)); 6 }
1 public static void main(String[] args) { 2 //判断手机号码是否正确 3 String str = "12678049802"; 4 String regex = "^1[3|4|5|7|8]\\d{9}$";//手机号码以1开头,第二位可以是3、4、5、7、8总共11位 5 System.out.println(str.matches(regex)); 6 }
正则表达式的具体应用实例:https://baike.baidu.com/item/正则表达式/1700215
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
根据腾讯课堂任小龙老师的视频总结而来,侵删。