java基础知识点

java基础知识点记录

1,作用域public,private,protected,以及不写时的区别

地址格式

实际上,把重要的数据修饰为private,然后写一个public的函数访问它,正好体现了OOP的封装特性,是OOP安全性的体现

访问权限修饰符修饰类时需要注意:

1、不能用protected和和private修饰类

2、用friendly修饰的类叫友好类,在另外一个类中使用友好类创建对象时,要保证它们在同一包中

2,关于String s2=new String( “Hello “)创建几个对象的分析

借鉴的一篇经典博客解释

视情况而定,当常量池中没有“Hello”对象时是创建两个,一个存放在常量池中,另一个存放在堆(内存)中(new String 产生)

并把这个堆内存中的String对象的引用返回给s2,String s2是栈中创建了的一个变量

如果是String s = “xyz”,那就要看常量池里有没有”xyz”,如果有直接引用,如果没有则创建再引用

3,==与equals

地址格式

public class test1 {
public static void main(String[] args) {
	String a = new String("ab"); // a 为一个引用
	String b = new String("ab"); // b为另一个引用,对象的内容一样
	String aa = "ab"; // 放在常量池中
	String bb = "ab"; // 从常量池中查找
		if (aa == bb) // true
			System.out.println("aa==bb");
		if (a == b) // false,非同一对象
			System.out.println("a==b");
		if (a.equals(b)) // true
			System.out.println("aEQb");
		if (42 == 42.0) { // true
			System.out.println("true");
     }
  }
}

地址格式

4,抽象类和接口

讲解得很好地一篇博客

抽象类

     1、抽象类不能被实例化,实例化的工作应该交由它的子类来完成,它只需要有一个引用即可。
    
     2、抽象方法必须由子类来进行重写。
    
     3、只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。
    
     4、抽象类中可以包含具体的方法,当然也可以不包含抽象方法。
    
     5、子类中的抽象方法不能与父类的抽象方法同名。
    
     6、abstract不能与final并列修饰同一个类。
    
     7、abstract 不能与private、static、final或native并列修饰同一个方法。、 `接口`

     1、个Interface的方所有法访问权限自动被声明为public。确切的说只能为public,当然你可以显示的声明为protected、private,但是编译会出错!
    
     2、接口中可以定义“成员变量”,或者说是不可变的常量,因为接口中的“成员变量”会自动变为为public static final。可以通过类命名直接访问:ImplementClass.name。
    
     3、接口中不存在实现的方法。
    
     4、实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。
    
     5、不能使用new操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。可以使用 instanceof 检查一个对象是否实现了某个特定的接口。例如:if(anObject instanceof Comparable){}。
    
     6、在实现多接口的时候一定要避免方法名的重复。

5,重载和重写

a,重载

概念:对于同一个类,如果这个类里面有两个或者多个重名的方法,但是方法的参数个数、类型、顺序至少有一个不一样,这时候局构成方法重载

作用:方法重载就是对不同数据类型的的数据实现相似的操作

说明:在Test类中有两个方法,名字都相同,都是a。在调用方法a时,如果不传参数,则系统会自动调用第一个方法a;如果传入一个 int 类型的参数,则系统调用第二个方法a

//example1
public class Test{  
  public void a( ){  
   };  
   public void a( int i){  
   };  
}  
//example2
public class Test{  
  public void a(int i,String j){  
  }  
  public class a(String j,int i){  
  }  
}  
b,重写

概念:当一个子类继承一父类,而子类中的方法与父类中的方法的名称,参数个数、类型都完全一致时,就称子类中的这个方法重写了父类中的方法。重写也是覆盖 override

前提:需要有继承关系

特点:当子类重写了父类的函数,那么子类的对象如果调用该函数,一定调用的是重写过后的函数;可以通过super关键字进行父类的重写函数的调用;继承可以使得子类增强父类的方法

细节:

1函数名必须相同

2参数列表必须相同

3子类重写父类的函数的时候,函数的访问权限必须大于等于父类的函数的访问权限否则编译报错

4子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类,不能返回比父类更大的数据类型

//examples

class Animal{
	int x=1;
	String name;
	
	void eat(){
		System.out.println("吃东西");
	}
	void shout(){
		System.out.println("我是动物");
	}
}
 
class Dog extends Animal{
	
	
	void eat(){
		System.out.println("啃骨头");
	}
	void shout(){
		System.out.println("旺旺");
	}
	void eat(String food){
		System.out.println("吃:"+food);
	}
}
class Cat extends Animal{
 
	void eat(){
		System.out.println("吃老鼠");
	}
	void shout(){
		System.out.println("喵喵");
	}
}
 
class Demo9{
 
	public static void main(String[] args){
		Dog d=new Dog();
		d.shout();
		d.eat();
		
		Cat c=new Cat();
		c.shout();
		c.eat();
		System.out.println();
     	}
//原文:https://blog.csdn.net/qq_32575047/article/details/79949823 
c,重载和重写的不同

1:重载(overload):

前提: 所有的重载函数必须在同一个类中

特点: 函数名相同,参数列表不同,与其他的无关(访问控制符、返回值类型)

不同:个数不同 、 顺序不同、 类型不同

重写(override):

前提: 继承

特点:函数名必须相同、参数列表必须相同;子类的返回值类型要等于或者小于父类的返回值类型

d,构造器能否被重写

构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以根本谈不上继承 又由于构造器不能继承,所以就不能被重写。但是,在同一个类中,构造器是可以被重载的

6,List

  1. 列表可包含任何数据类型的元素,单个列表中的元素无须全为同一类型。
  2. append() 方法向列表的尾部添加一个新的元素。
  3. 列表是以类的形式实现的。“创建”列表实际上是将一个类实例化。因此,列表有多种方法可以操作。extend()方法只接受一个列表作为参数,并将该参数的每个元素都添加到原有的列表中。

7,forward和redirect的区别

转发是服务器行为,重定向是客户端行为

forward(转发)

是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,因为这个跳转过程是在服务器实现的,并不是在客户端实现的所以客户端并不知道这个跳转动作,所以它的地址栏还是原来的地址.

redirect(重定向)

是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.

8,多线程

地址格式

Runnable对象作为Thread对象的target,Runnable实现类里面包含run方法仅仅作为执行体。也就是说Thread类的作用是把run方法包装成线程的执行体

实际运行的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run方法

调用的是哪个run方法

//代码1
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Run of Runnable");  
    }  
}) {  
    public void run() {  
        System.out.println("Run of Thread");  
    }  
}.start();
//代码2
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Run of Runnable");  
    }  
}) {  
    public void run() {  
        System.out.println("Run of Thread");  
        super.run();  
    }  
}.start();

JDK的Thread源码,片段 3

private Runnable target; 
public void run() {  
    if (target != null) {  
        target.run();  
    }  
}  

在run()方法中,首先会检查target是否为空,如果不是,则执行该target的run()方法。

那么,对于上面两段代码的执行,也就清楚了。

在第一个代码段 1 中,重写了Thread的run()方法,同时传入了一个Runnable对象,该对象也实现了run()方法。该Thread对象调用start()方法后,会执行该对象重写的run()方法,其输出结果也就是Run of Thread,输出完后,run()方法返回,该线程对象的生命周期也就结束了。

在第二个代码段 2 中,同样也重写了Thread的run()方法,同时传入了一个Runnable对象,实现了run()方法。唯一不同的是,在Thread重写的run方法中,在打印输出后,还执行了super.run(),这就有意思了。

首先,该线程启动运行后,执行其重写的run()方法,输出Run of Thread。

接下来调用super.run(),也就是调用超类的run()方法,而该超类的run()方法,也就是JDK定义的Thread类的run(),其执行如上代码段 3 所示;显然target不为空,这时候会调用该对象的run()方法,会输出Run of Runnable.。

如果,上面的Thread并未重写run()方法,那么,执行的结果还是一样。首先会执行该Thread的run()方法,因为此时并未重写该方法,所以还是会调用JDK定以的run()方法,也就是上面的代码段 3,在该代码段中会判断target是否为空,显然不是,所以会调用Runnable对象实现的run()方法。

总结:对于Thread(Runnable target …),不管传入的Target是否为空,首先都会执行Thread自己的run()方法。如果重写了该方法且该方法中没有super.run(),那么是永远不会调用Runnable实现的run()方法;如果没有重写该方法,则会去判断target是否为空,以此来决定调用target实现的run()方法;如果重写了该方法,且该方法中有super.run(),在执行完该语句之前的所有代码后,会判断target是否为空,以此来决定调用target实现的run()方法,执行完后,接着执行该语句之后的代码。

9,HashMap的基本用法

  • 创建HashMap对象
	HashMap<String,Integer> hashMap = new HashMap<>();
  • 添加键值对
    hashMap.put("aa",1);
    hashMap.put("bb",2);
    hashMap.put("cc",3);
//添加元素时,如果key已经存在,则返回旧value,并将新的value存到该key中;如果key不存在,则返回null

put方法会覆盖原有的value,而另一种put方法不会覆盖:putIfAbsent(key,value)

 hashMap.putIfAbsent("aa",4);

该方法首先会判断key是否存在,如果存在且value不为null,则不会覆盖原有的value,并返回原来的value;如果key不存在或者key的value为null,则会put进新值,并返回null。

另外,两种方法当key=null时,并不会抛出异常,而是按照一个特殊的方法进行存储

  • 删除元素

remove(key):删除成功(存在key),返回被删除的key对应的value,否则返回null。

remove(key,value):删除成功(存在entry),返回true,否则返回false。

hashMap.remove("bb");
hashMap.remove("aa",5);
  • 获取元素

对于获取元素,有get(key)和getOrDefault(key,defaultValue)(1.8之后)两种方法.

hashMap.get("cc")

getOrDefault在key不存在时,返回一个defaultValue。在没有该方法前需要这样写:

 Integer bbValue = hashMap.containsKey("bb")?hashMap.get("bb"):-1;

有了getOrDefault可以这样写:

getOrDefault("aa",-1)//key=aa不存在,所以返回默认value -1
  • 元素遍历
Iterator iterator1 = hashMap.entrySet().iterator();
        while (iterator1.hasNext()){
            Map.Entry entry = (Map.Entry) iterator1.next();
            String key = (String) entry.getKey();
            Integer value = (Integer) entry.getValue();
            System.out.println(key+"="+value);
        }
  • 判断key或value是否存在
hashMap.containsKey("aa");

hashMap.containsValue(1);
  • 替换元素

replace方法用来替换元素。

  hashMap.replace("ff",5);

对于存在的key,调用replace方法,会替换原来的value,并返回旧value,这和put的效果是一样的;对于不存在的key,replace方法什么都不做。这就是他和put的区别(put在key不存在时将新key-value加入map)

10,Map的使用

HashMap,TreeMap,LinkedHashMap的简单应用

HashMap是随机的,TreeMap是按照元素的大小顺序排列的,LinkedHashMap是按照添加的顺序排列的。

package com.lxk.collectionTest;
 
import com.google.common.collect.Maps;
 
import java.util.Map;
 
/**
 * 测试Map是否有序的区别
 * <p>
 * Created by lxk on 2017/5/24
 */
public class OrderedMapTest {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        Map<String, Integer> treeMap = new TreeMap<>();
        Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
        System.out.println("--------------test hashMap");
        testMap(hashMap);
        System.out.println("--------------test treeMap");
        testMap(treeMap);
        System.out.println("--------------test linkedHashMap");
        testMap(linkedHashMap);
    }
 
    private static void testMap(Map<String, Integer> map) {
        map.put("asd", 1);
        map.put("2das", 2);
        map.put("3das", 3);
        map.put("4das", 4);
        //这也是map的一种遍历方式,增强for
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
 
}

结果如下:

地址格式