java----语言基础
重载:
//同一个类中,方法名相同,参数列表不同[java就是靠不同的参数列表来寻找方法的],返回值可以任意 public class Demo { public static void main(String[] args) { float num1 = 3; float num2 = 4; float x = add(num1,num2); System.out.println(x); } public static int add(int a,int b){ return a+b; } public static float add(float a,float b){ return a*b; } }
可变参数:
sayhi(String... str){ } 此时的str可以传递,也可以不传递。但是可变参数必须放在最后面。它本身是一个数组
static:
static修饰字段
使用static关键字修饰一个字段:声明的static变量实际上就是一个全局变量
static修饰的方法不可以重写,所以接口和抽象方法中定义的static方法必须是需要方法体的。另外接口中定义的变量必须附初始值,抽象类定义的变量可以不需要附初始值。
使用static关键字修饰一个方法:可以直接使用类调用方法,和对象没有关系了
使用static关键字修饰一个类:内部类
如果需要了解跟多static关键字修饰字段,参考我的另一篇博客:https://www.cnblogs.com/yanxiaoge/p/10658660.html
public class Demo { public static void main(String[] args){ System.out.print(Test.name);//一般定义静态字段,最好又类来调用; //System.out.print(Test.name1); //只有定义了静态字段,类才可以直接调用 } } class Test{ String name1 = "dddsdfsd"; static String name = "dd"; }
static修饰方法
public class Demo { public static void main(String[] args){ Test.setCountry("中国"); Test h = new Test(); System.out.print(h.country); } } class Test{ static String country; public static void setCountry(String country){ //静态方法只能访问静态字段;不能访问非静态字段 Test.country = country; } }
static代码块和构造代码块:
如果Test类继承了父类,会先执行父类的静态代码块和构造代码块(如果是创建对象的情况下)
static{}:代码块是类的加载过程中初始化阶段执行的。和new对象没有任何关系,比如Test.静态属性,也同样会触发static{},只是在new 对象过程中,如果发现当前类还没有被加载到内存,就会将类加载到内存,在初始化过程中执行了<clinit>() 方法,如果static{}中有打印信息,就会被打印出来,如果第二期new 对象的时候,发现类已经加载内存了,就不会再进行初始化了.也就是为什么多次new 对象,static 变量只有一个的原因了(全局共享),而且这个变量就和对象没有任何关系了(但是还是可以通过对象访问静态变量和静态方法的,一个对象对静态变量的修改会影响到另一个对象)
public class Demo { public static void main(String[] args){ Test t = new Test(); Test t1 = new Test(); } } class Test{ static{ System.out.println("静态代码块");//在类中使用static定义,最先执行,只会创建一次(当创建第二个对象是,不会再执行该代码,static只存储一次) } { System.out.println("构造代码块");//在类中定义,优先构造方法执行,每次创建对象都会执行 } public Test(){ { System.out.println("普通代码块"); //在方法中定义,方法被调用执行 } System.out.println("我是构造方法"); } }
通过类调用静态方法只会调用静态代码块,多次调用也会执行一次静态代码块。(如果继承父类,同样会先执行父类的)
public class Demo { public static void main(String[] args){ Test.t(); Test.t(); } } class Test{ static{ System.out.println("静态代码块");//在类中使用static定义,最先执行,只会创建一次(当创建第二个对象是,不会再执行该代码,static只存储一次) } { System.out.println("构造代码块");//在类中定义,优先构造方法执行,每次创建对象都会执行 } public Test(){ { System.out.println("普通代码块"); //在方法中定义,方法被调用执行 } System.out.println("我是构造方法"); } public static void t(){ } }
Test t = null;不会有任何操作
调用通过类调用final修饰的字段(同时被static修饰,不然不能被类直接调用)不会初始化。
public class Demo { //main方法会被调用,所有static会被执行 static { System.out.println("x"); } public static void main(String[] args){ //这样不会执行Test中的任意代码块 Test t = null; } } class Test{ static{ System.out.println("静态代码块");//在类中使用static定义,最先执行,只会创建一次(当创建第二个对象是,不会再执行该代码,static只存储一次) } { System.out.println("构造代码块");//在类中定义,优先构造方法执行,每次创建对象都会执行 } public Test(){ { System.out.println("普通代码块"); //在方法中定义,方法被调用执行 } System.out.println("我是构造方法"); } }
下面输出结果:构造。构造。静态。构造
public class B { public static B t1 = new B(); public static B t2 = new B(); { System.out.println("构造块"); } static { System.out.println("静态块"); } public static void main(String[] args) { B t = new B(); } }
final:
private static final int NUM = 10; //final表示该字段为常量,通常全部大写
final 修饰一个类,则该类不能被继承;final不可以修饰抽象类和接口
final 修饰一个方法不能重写,和static一样。
将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。
接口中的变量都是final
常量类
public class Demo { public static void main(String[] args){ Person person = new Person(11); } } class Constant{ //将常量专门放在一个类中,让其他的类继承他[常量不一定必须初始化,可以在构造器中默认赋值(如果采用这种方法给常量赋值,常量不能用static修饰)] public static final int NUM = 10; //加上static,目的不用创建了对象,就可以直接调用常量; } class Person extends Constant{ private final int NUM; public Person(int id){ NUM = id; //在构造器中的初始化 } { System.out.println(Constant.NUM); } }
volatile:
背景
数据的计算(1+1),首先是从内存中加载代cpu缓存中,在加载到cpy计算。cpu计算完成返回的结果放到cpy缓存区,至于什么时候在返回到内存,是不确定的,看cpu空闲时间。有没有方法能让计算结果立刻返回到内存中。
volatile是Java提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色。同synchronized相比(synchronized通常称为重量级锁),volatile更轻量级,相比使用synchronized所带来的庞大开销,但是注意 volatile不能确保原子性,在访问volatile变量时,不会执行加锁操作,因此也就不会执行线程阻塞。所以volatile变量是一种比sychhronized关键字更轻量级的同步机制。
原子性:原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1;
volatile的适用场景:
1、对变量的写入操作不依赖其当前值(如 number++ 、count = count * 5等)
2、该变量没有包含在具有其它变量的不变式中
3、volatile关键字另一个作用就是禁止指令重排优化
参考:https://www.cnblogs.com/xuwenjin/p/9051179.html
https://www.cnblogs.com/chengxiao/p/6528109.html
补充可以看看CAS:https://blog.csdn.net/v123411739/article/details/79561458
面对对象:
对象被创建的步骤
1.分配对象空间,并将对象成员变量初始化为0或空
2.执行属性值的显式初始化
3.执行构造方法(此时的对象已经被创建,我们可以在这一步对对象进行进一步的初始化工作)
4.返回对象的地址给相关的变量
public class Demo { public static void main(String[] args){ Horse h = null; h = new Horse(); // h.name = "dd"; h.eat(); h = null; //释放对象 } } class Horse{ String name; public void eat(){ System.out.println(name); } }
封装性:
public class Demo { public static void main(String[] args){ Horse h1 = new Horse(); // h1.name = "小明";//由于 name设置了私有字段,此时在给对象赋值,会报错; h1.setName("小明"); System.out.println(h1.getName()); } } class Horse{ //属性的封装 private String name; private int age; //成员变量,在类中定义,在堆内存,对象清空,则成员变量清空,有默认初始值,像String引用变量初始值为null,int的初始值为0; public void setName(String name){ //参数也是局部变量 int a = 111; //局部变量;在方法中定义,在栈内存,方法调用完毕,就清空,没有初始值,必须定义,赋值后,才能调用。 this.name = name; } public String getName(){ // return name; return this.name; // 可以使用this调用其他方法和属性,this可以省略,this前面可以加上类名 Horse.this.name(); } }
构造方法和构造方法的重载:
类似python中的__init__(self){}
public class Demo { public static void main(String[] args){ Horse h = new Horse(); Horse h2 = new Horse(1); } } class Horse{ public Horse(){ //方法名称和类名相同,没有返回值 System.out.println("我是构造方法"); } public Horse(int a){ System.out.println("a="+a); } }
构造方法之间的调用,用this()
public class Demo { public static void main(String[] args){ Horse h2 = new Horse(1); } } class Horse{ public Horse(){ //默认构造方法,可以保留 } public Horse(String b){ //方法名称和类名相同,没有返回值 System.out.println(b); } public Horse(int a){ this("11"); //调用另一个构造方法,注意,这条语句,只能放在最前面,否则报错;,限制了,不能调用多个; this("22"); //报错,只能调用一个 System.out.println("a="+a); } }
对象之间的关联:
public class Demo { public static void main(String[] args){ Hero hero1 = new Hero("刘备"); Weapon weapon1 = new Weapon("双股剑"); hero1.setWeapon(weapon1);//将对象传递给一个对象,实现对象之间的关联 System.out.println(hero1.getWeapon().getName()); } } class Hero{ //属性的封装 private String name; private Weapon weapon; public Hero(String name){ this.name = name; } public void setWeapon(Weapon weapon){ this.weapon = weapon; } public Weapon getWeapon(){ return this.weapon; } } class Weapon{ private Hero hero; private String name; public Weapon(String name){ this.name = name; } public void setHero(Hero hero){ this.hero = hero; } public String getName(){ return this.name; } }
继承:
方法的重载:发生在同一个类中,函数名相同,参数列表不相同,与返回值返回值(类型)无关;
方法的重写:发生在继承的关系中,函数名相同,参数列表相同,返回值(类型)需要相同。子类的访问修饰符需要大于或者等于父类的访问修饰符,子类的异常声明要小于或等于父类的异常声明,如果方法被private 、static修饰,那么方法不能被继承/重写。 final修饰,只能继承,不能重写
public class Demo { public static void main(String[] args){ Dog dog = new Dog(); //子类创建对象时 ,也会 自动 调用父类的默认的构造方法 System.out.println(dog.name); dog.print(); } } class Animal{ protected String name = "动物"; public Animal(){ System.out.println("我是Animil 默认构造方法"); } public Animal(String name){ System.out.print("我是Animial 传参构造方法"); } public void print(){ System.out.println("Animal"); } } class Dog extends Animal{ //继承的写法 public Dog(){ super("传值"); //如果有super,就会继承super所指的构造方法,具体执行那一个,看传参的列表 // super(); //如果没有传值,就会执行父类的默认的构造方法,前提父类必须保留默认的构造方法; //注意1:默认情况下,会自动添加super(),如果自己写了super,就会覆盖默认的 //注意2:super(),只能方法最开始的位置 } public void print(){ super.print(); //继承父类的方法; System.out.println("Dog"); } }
抽象类:
1、抽象类可以没有抽象方法,有抽象方法的一定是抽象类
2、非抽象类继承抽象类必须实现所有的抽象方法
3、抽象类可以继承抽象类,可以不实现父类的抽象方法
4、抽象类可以有方法实现和属性
5、抽象类不能被实例化
6、抽象类不能声明final
7、抽象类可以有构造方法
public class Demo { public static void main(String[] args){ Dog dog = new Dog(); } } abstract class Animal{ //声明该类是一个抽象类,只能继承,不能实例化 { System.out.println("可以有构造方法"); } public abstract void eat(); //可以没有抽象方法 ,如果有了抽象方法,该类必须是抽象类,并且该抽象方法必须子类实现; public void run(){ //普通的方法可以不用被实现 System.out.print("我可以跑"); } } class Dog extends Animal{ public void eat(){ } }
抽象类设置了很多属性,这些属性有什么意义?
当我们子类继承父类,子类实例化的时候就可以使用super(),像父类传值。父类就可以在特定的场景使用这些属性。
当然,有些属性是由父类自己设置的。
接口:
1、抽象类实现接口,可以不实现接口的方法,非抽象类,必须实现接口的所有的方法;
2、接口的方法没有声明访问修饰符,默认为public
3、接口不能有构造方法,接口不能被实例化
4、接口之间的继承用extends ,定义接口用interface ,类实现接口用implements
5、接口只能定义常量,抽象方法,(jdk1.8之后有默认的实现方法,和静态方法)
6、final不可以修饰接口
public class Demo { public static void main(String[] args){ Person person = new Person(); person.print(); //默认的方法实现; } } interface IEat{ // public abstract void eat(); void eat(); //简写,定义抽象方法 // public final static int NUM = 10; int NUM = 10; //简写,定义常量 public default void print(){ System.out.println("默认方法实现"); //jdk1.8之后有 默认方法实现(加上default),不能有其他的方法实现 } } interface ISleep{ void sleep(); } interface IRun extends IEat,ISleep{ //接口可以有多继承 void run(); } class Person implements IRun,ISleep{ //接口的实现,类可以继承多个接口(不是接口的继承) public void sleep(){} //必须实现接口的所有的方法 public void run(){} public void eat(){} }
多态:
public class Demo { public static void main(String[] args){ Chick smailchick = new SmailChick(); //用父类的引用指向子类对象,(用大的类型接受小的类型,向上转型,自动转换) eat(smailchick); SmailChick smailchick2 = new SmailChick(); eat(smailchick2); Chick bigchick = new BigChick(); eat(bigchick); } public static void eat(Chick c){ //多态性,Chick可以接受比他小的或者等于他的类型,(例如int可以接受short类型) c.eat(); // c.song(); //直接写会报错,原因Chick中没有song方法,在运行的时候,c才是所new的对象,在编译的时候c还是chick if(c instanceof BigChick){ //在转换之前做类型的判断 // BigChick bigchick = (BigChick)(c); //若果父类中没有定义song()方法,就需要 强制转换 将Chick转变成BigChick // bigchick.song(); ((BigChick)c).song(); //简写 } } } abstract class Chick{ public abstract void eat(); } class SmailChick extends Chick{ public SmailChick(){ super(); }//默认自动添加 public void eat(){ System.out.println("我是小鸡,我爱吃米"); } } class BigChick extends Chick{ public void eat(){ System.out.println("我是大鸡,我爱吃虫"); } public void song(){ System.out.println("我是大鸡,我会唱歌"); } }
this的本质
指明当前的对象,不能放到static静态方法中
class Test{ int a; public Test(int a){ //给成员变量进行赋值 this.a = a; } public Test(){ //调用另一个构造方法,只能放在代码的第一行(所以只能有一个) this(1); } }
java中的new到底为我们做了什么?
参考https://www.cnblogs.com/KingIceMou/p/7245446.html
补充
访问修饰符
内部类:
内部类的好处
可以使用外部类的元素
class Demo extends B{ private String a; class Test{ public void test() { System.out.println(a); } } }
匿名内部类可以加private protected修饰,隐藏对象
public class Demo { public static void main(String[] args){ A a = new A(); //实现隐藏 Instance in = a.getIn(); } } interface Instance{} class A{ public B getIn(){ return new B(); } private class B implements Instance{ } }
可以实现多重继承
class A { public String name() { return "小明"; } } class B { public int age() { return 18; } } class Test { private class test1 extends A { } private class test2 extends B { } public String name() { return new test1().name(); } public int age() { return new test2().age(); } public static void main(String args[]) { Test mi = new Test(); System.out.println("姓名:" + mi.name()); System.out.println("年龄:" + mi.age()); } }
避免修改接口而实现同一个类中两种同名方法的调用
你的类要继承一个类,还要实现一个接口,可是你发觉你继承的类和接口里面有两个同名的方法怎么办?
未使用内部类的情况
interface A{ void test(); } class B{ void test(){ System.out.println("B"); } } class Demo extends B implements A{ @Override public void test() { //这种方法就调用不到B类的方法了 } }
使用内部类的情况
interface A{ void test(); } class B{ void test(){ System.out.println("B"); } } class Demo extends B{ //使用内部类来实现接口 class Test implements A{ @Override public void test() { } } }
成员内部类:
public class Demo { public static void main(String[] args){ A a = new A(); // A.B b = a.new B(); //创建内部类,通常不建议这样调用 // b.print(); a.Bprint(); //通过接口来创建内部类,并调用类的方法 } } class A{ //成员内部类 public void Bprint(){ B b = new B(); b.print(); } //成员内部类,建议设置私有.内部调用 private class B{ public void print(){ System.out.println("测试"); } } }
方法内部类:
public class Demo { public static void main(String[] args){ A a = new A(); a.Aprint(); } } class A{ //方法内部类 public void Aprint(){ int x = 1; // x++; //由于内部类调用了局部变量x,所以x不能修改; System.out.println(x); class B{ public void print(){ System.out.println("测试"+x); //一旦方法内部类中调用了局部变量,该变量默认是final,不可以被修改; } } B b = new B(); //方法内部类只能在该方法内实例化 b.print(); } }
解释:为什么方法内部类中类调用局部变量,该变量必须修饰为final(在jdk1.8之后,可以不用加,解释器会默认加上)?
当上面的Aprint方法结束了。方法直接出栈,栈中里面的局部变量清空,但是对象(内部类),可能GC垃圾回收机制,还没有将对象回收掉。而对象有调用着局部变量,此时就会报错,所以必须将该局部变量修饰为final,放在堆的常量区,这个即使方法出栈了,局部变量也不会被清空,而修饰了finnal的局部变量,则不能在进行修改操作了。
注意:没有被类调用的变量不会默认为final,可以进行修改操作;
内部类中可以使用不加final的静态变量
静态内部类:
静态内部类不依赖对象,所以开发项目的时候优先考虑
使用静态内部类不用担心内存泄漏的问题;
public class Demo { public static void main(String[] args){ A.B b = new A.B(); b.print(); } } class A{ //静态内部类 ,不能调用非静态的方法和属性; static class B{ public void print(){ System.out.println("静态内部类"); } } }
匿名内部类:
继承式匿名内部类
接口式匿名内部类
参数式匿名内部类
注意事项:
1、不能有构造方法(没有名字),只能有一个实例
2、不能定义任何静态成员,静态方法(内部类依赖对象,对象和静态是没有关系的)
3、不能是private static protected public (没有类,没办法加修饰符)
4、一定是是在new后面,用其隐含实现一个接口或者继承一个类
5、匿名内部类是局部的,所以局部内部类的所有的限制都对其生效
public class Demo { public static void main(String[] args){ print1(); print2(); print3(new C(){ public void print(){ System.out.println("参数式内部类"); } }); } public static void print1(){ A a = new A(){ public void print(){ System.out.println("继承式内部类"); } };//注意一定要有分号; a.print(); } public static void print2(){ B b = new B(){ public void print(){ System.out.println("接口式内部类"); }; }; b.print(); } public static void print3(C c){ c.print(); } } abstract class A{ public abstract void print(); } interface B{ void print(); } abstract class C{ //interface class C{} 一样 public abstract void print(); }
总结:
每个内部类能独立的继承或一个(接口)的实现,所以无论外部类是否是否已经继承了某个(接口的)实现,对于内部类没有影响。
并且内部类为多继承的实现,提供了解决方案;
public class Demo { public static void main(String[] args){ C c = new C(); c.print(); } } abstract class A{ } abstract class B{ public abstract void print(); } class C extends A{ class D extends B{ public void print(){ System.out.print("测试"); } } public void print(){ D d = new D(); d.print(); } }
内部类访问外部内对象属性
class A{ private User user; class B{ //如果需要访问user User u = A.this.user; } }
父子类之间转换:
Father f1 = new Father(); Father f2 = new Son(); Son s1 = new Son(); //子类强转父类,不会有任何问题,只是不能在调用子类的方法,如果想调用子类的方法,之间在转换成子类就行,因为这个对象本身就是子类,本质没有改变,所以我们看方法都是用父类接受对象,在方法中在对对象进行转换成子类(判断是不是属于子类) (Father)s1 (Son)(Father)s1 //f2本身就是子类,f1本身就是父类 (Son)f1 报错 (Son)f2 不报错